2016-05-24 12 views
7

Tôi đang đối phó với một số mã C mà mất một số dữ liệu, và chuyển đến chức năng thông qua tại:Các tham số lambda của tôi có thực sự che giấu người dân địa phương của tôi không?

void foo(int* data, void (*fun)(int*)){ 
    (*fun)(data); 
}; 

Các tác phẩm sau đây mà không cảnh báo:

void bar(int* data){}; 

int main(){ 
    int data=0; 
    foo(&data,bar); 
} 

Tuy nhiên, nếu tôi sử dụng một lambda thay vì:

int main(){ 

    int data=0; 
    foo(&data,[](auto data){}); 
} 

tôi nhận được cảnh báo sau đây:

warning: declaration of ‘data’ shadows a previous local [-Wshadow] 
    foo(&data,[](auto data){}); 
         ^
o.cpp:14:7: note: shadowed declaration is here 
    int data=0; 

Nhưng tôi nghĩ một nhóm chụp rỗng sẽ loại trừ instantiation đầu tiên trong hình dáng của nó lên.

là cảnh báo này hợp pháp?
Tại sao không phải là chụp trống đủ để tránh cảnh báo?

+0

Tôi nghĩ cảnh báo chỉ ở đó để cảnh báo bạn rằng bạn có thể nghĩ mình đang sử dụng 'dữ liệu' chứ không phải' dữ liệu'. Nó không biết ý định * của bạn là gì. – vu1p3n0x

+3

_name_ thực sự bị che khuất. Bạn không cần chụp một biểu tượng để sử dụng tên của nó, ví dụ: trong một ngữ cảnh không được đánh giá như 'decltype'. – ildjarn

+0

Ít nhất trong Visual Studio 2015, bạn * không thể * sử dụng các ký hiệu trong một ngữ cảnh không được đánh giá (thậm chí decltype (dữ liệu) hoặc sizeof (dữ liệu) không thành công với một lỗi 'C2065: 'data' undeclared identifier') –

Trả lời

4

Names từ phạm vi bao quanh của lambda cũng nằm trong phạm vi của lambda.

Tên không bị bắt vẫn có thể được sử dụng, miễn là chúng không phải là không được sử dụng. Chỉ các biến số odr được sử dụng mới phải được chụp. Ví dụ:

#include <iostream> 

template<typename T> void foo(const int *, T f) { std::cout << f(5) << '\n'; } 

int main() 
{ 
    const int data=0; 
    foo(&data,[](int baz){ 
     return data; 
    }); 
} 

Bởi vì đọc một biểu thức hằng số không phải là ODR sử dụng, mã này là chính xác và data đề cập đến biến trong main.

Chương trình này xuất ra 0, nhưng nếu bạn thay đổi int baz thành int data, kết quả sẽ xuất ra 5.

+0

Danh sách chụp trống không chụp bất kỳ biến nào từ phạm vi hiện tại (xem tại đây http://en.cppreference.com/w/cpp/language/lambda) Visual Studio 2015 không biên dịch ví dụ của bạn và không thành công với lỗi C3493 (không nắm bắt tiềm ẩn 'dữ liệu' nếu không phải kiểu chụp mặc định được đưa ra) –

+0

@AndreasH Điểm của câu trả lời của tôi là các tên đó nằm trong phạm vi và có thể được sử dụng theo các cách hạn chế mặc dù chúng không bị bắt giữ –

+0

Điều này có nghĩa là tôi có thể viết [] (int baz) { return sizeof (dữ liệu);} vì đây không phải là việc sử dụng dữ liệu (vì vậy không cần phải nắm bắt) và nó vẫn nằm trong phạm vi của lambda - hoặc tôi thiếu gì đó? –

0

Tham chiếu đến MISRA C++ 2008: Mã định danh được khai báo trong phạm vi bên trong sẽ không bao giờ có cùng tên với số nhận dạng được khai báo trong phạm vi bên ngoài.

Trong ví dụ của bạn int data được khai báo trong phạm vi bên ngoài nhưng đi vào phạm vi bên trong của lambda của bạn thông qua tham khảo. Vấn đề là bạn cũng có một tham số trong danh sách tham số của lambda của bạn được gọi là dữ liệu (phạm vi bên trong). Điều này dẫn đến việc ẩn biến dữ liệu từ phạm vi bên ngoài trong lambda.

Nhân tiện. Ngoài ra, ví dụ đầu tiên của bạn với con trỏ hàm sẽ được viết lại vì cũng có xung đột với việc đặt tên các số nhận dạng trong phạm vi bên trong và bên ngoài. Trong trường hợp này nó không thực sự xấu gây ra hiệu quả chỉ có một biến dữ liệu được sử dụng trong số điểm bên trong. Tuy nhiên, khi các biến danh sách tham số và các biến từ phạm vi bên ngoài gọi hàm có cùng tên thì điều này có thể dẫn đến nhầm lẫn lập trình và cũng nên tránh.

Các vấn đề liên quan