2014-09-17 16 views
11

tôi có mã này:Không thể sử dụng gõ một cách rõ ràng lambda

std::function<std::string&(std::string&)> change_str = [](std::string& str){ 
    return (str = "Hello world!"); 
}; 

std::string s; 

std::cout << change_str(s) << std::endl; 

Nó không biên dịch, và nói:

main.cpp:8:47: error: no viable conversion from '(lambda at main.cpp:8:60)' to 'std::function<std::string &(std::string &)>' 
    std::function<std::string&(std::string&)> change_str = [](std::string& str){ 
              ^   ~~~~~~~~~~~~~~~~~~~~~ 
/usr/include/c++/v1/functional:1448:5: note: candidate constructor not viable: no known conversion from '(lambda at main.cpp:8:60)' to 'nullptr_t' for 1st argument 
    function(nullptr_t) _NOEXCEPT : __f_(0) {} 
    ^
/usr/include/c++/v1/functional:1449:5: note: candidate constructor not viable: no known conversion from '(lambda at main.cpp:8:60)' to 'const std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &)> &' for 1st argument 
    function(const function&); 
    ^
/usr/include/c++/v1/functional:1450:5: note: candidate constructor not viable: no known conversion from '(lambda at main.cpp:8:60)' to 'std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > &)> &&' for 1st argument 
    function(function&&) _NOEXCEPT; 
    ^
/usr/include/c++/v1/functional:1454:41: note: candidate template ignored: disabled by 'enable_if' [with _Fp = (lambda at main.cpp:8:60)] 
             __callable<_Fp>::value && 
             ^
main.cpp:8:60: note: candidate function 
    std::function<std::string&(std::string&)> change_str = [](std::string& str){ 
                 ^
1 error generated. 

Tuy nhiên nếu tôi thay đổi tuyên bố std::function-auto, sau đó nó hoạt động:

auto change_str = ... 

Tại sao loại rõ ràng không làm việc cho lambda?

+0

Lưu ý rằng 'std :: function' không phải là" lambda được nhập rõ ràng ". Nghĩa là, 'std :: function' không chỉ dành cho lambdas (cũng vậy, lambdas không phải là đặc biệt). Nó có thể lưu trữ mọi 'f' có thể được gọi là' f (args) '. – milleniumbug

Trả lời

14

Một lambda không có kiểu trả về là auto, và tự động loại bỏ các tài liệu tham khảo bên ngoài, do đó bạn không trở về string& nhưng chỉ string.

Chỉ cần khai báo chức năng như

std::function<std::string&(std::string&)> change_str = 
[](std::string& str) -> string& ///<--- NOTE THIS 
{ 
    return (str = "Hello world!"); 
}; 
+4

* "Một lambda không có kiểu trả về là' auto' "* Có nghĩa là, trong C++ 14, chúng ta có thể sử dụng' -> decltype (auto) ', nhưng điều đó có thể được coi là tối nghĩa. – dyp

+0

@dyp: vấn đề là 'tự động' được áp dụng cho' decltype' là tham chiếu không giống kiểu tự động không phải là tham chiếu ... Có: nó * tối nghĩa *, ít nhất cho đến khi nó trở thành một thành ngữ cũng được thành lập ... vào năm 2017 –

4

suy luận kiểu trả về cho lambda của bạn là std::string, đó là lý do tuyên bố của bạn không phù hợp. Nhưng khi bạn xác định rõ ràng kiểu trả về, nó hoạt động:

std::function<std::string&(std::string&)> change_str = 
     [](std::string& str) -> std::string& 
{ 
    return (str = "Hello world!"); 
}; 
2

Một lambda không có kiểu trả về cư xử như auto mà theo Template Argument Deduction quy tắc và kiểu trả về của bạn là suy luận được std::string và không std::string&

Nếu loại được chỉ định rõ ràng mọi thứ đều tốt đẹp

std::function<std::string&(std::string&)> change_str = 
           [](std::string& str) -> std::string& { 
    return (str = "Hello world!"); 
}; 
2

Như những người khác nói, vấn đề là khấu trừ loại khấu trừ mặc định trừ std::string không phải là com có thể kiểm tra với số dự kiến ​​std::string&.

Catalogue của tờ khai khác nhau để giải quyết việc này:

// be completely explicit about the return type 
[](std::string& str) -> std::string& { 

// be explicit about returning lvalue reference 
[](std::string& str) -> auto& { 

// be explicit that you're returning some kind of reference type, 
// but use reference collapsing to determine if it's an lvalue or rvalue reference 
[](std::string& str) -> auto&& { 

// use C++14 feature to deduce reference type 
[](std::string& str) -> decltype(auto) { 

Những được liệt kê theo thứ tự nhất với hầu hết các chung. Tuy nhiên trong trường hợp này không có nhu cầu cụ thể về tính tổng quát: bạn chỉ suy ra kiểu trả về vì đó là kiểu gõ mặc định/ít nhất. Trong số này có lẽ tôi muốn nói là rõ ràng có lẽ là tốt nhất: [](std::string &str) -> std::string& {

quantdev xóa câu trả lời của ông mà tôi nghĩ làm một gợi ý tốt:

[](std::string& str) { 
    return std::ref(str = "Hello world!"); 
}; 

này hoạt động vì std::function chỉ yêu cầu chuyển đổi phù hợp với/từ các kiểu đối số và trả về, và trả về kết quả của std::ref ở đây đáp ứng yêu cầu đó.

Cả hai sử dụng std::ref và sử dụng loại trả về rõ ràng là std::string & có vẻ dễ đọc đối với tôi. Với tối ưu hóa trên thực hiện của tôi sản xuất chính xác cùng một điều cho cả hai, vì vậy nếu bạn thích giao diện của std::ref có ít lý do để không sử dụng nó.

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