Gọi tới lambda là mơ hồ mặc dù nói rõ loại trả về


11

Một hàm quá tải sẽ đưa cả hàm functor vào, với loại lambda là có thể quyết định (có thể chuyển thành một std::function(xin vui lòng sửa cho tôi nếu tôi sai). Câu hỏi là: Tại sao có lỗi biên dịch bên dưới, mặc dù loại lambda rõ ràng định nghĩa? ( [&]() -> Type {})

Xin lưu ý rằng, đối với giải pháp hiện tại của tôi, tôi cần tham chiếu bắt giữ, đó là lý do tại sao mã chứa logic cho nó.

Ví dụ sau mô tả vấn đề:

#include <iostream>
#include <string>    
#include <functional>

void do_some(std::function<void(int)> thing) 
{
   thing(5);
}

void do_some(std::function<bool(int)> thing)
{
   if (thing(10)) 
   {
      std::cout << "it's true!" << std::endl;
   }
}

int main()
{
   int local_to_be_modified = 0;
   do_some(
      [&](int in)
      {
         local_to_be_modified = in;
         std::cout << "This is void-" << std::endl;
      }
   );
   do_some(
      [&](int in) -> bool
      { 
         // error: call to 'do_some' is ambiguous
         local_to_be_modified += in;
         std::cout << "This is bool-" << std::endl;
         return true;
      }
   );
}

6
Bởi vì std::function<void(int)>có thể được xây dựng ngay cả từ lambda trả về một cái gì đó (khiến giá trị trả về bị bỏ qua).
HolyBlackCat

1
Bên cạnh đó, việc chỉ định rõ ràng kiểu trả về của lambda đó chính xác là không có gì.
Ded repeatator

Câu trả lời:


8

Bởi vì biểu thức lambda thứ 2 trở lại boolcó thể chuyển đổi thành cả hai std::function<void(int)>std::function<bool(int)>hoàn toàn.

std::function có một hàm tạo chuyển đổi:

template< class F >
function( F f );

Constructor này không tham gia vào giải quyết tình trạng quá tải nếu f là Callable cho loại đối số args ... và trở lại loại R. (vì C ++ 14)

Theo định nghĩa của Callable ,

Các biểu thức sau phải hợp lệ:

INVOKE<R>(f, std::declval<ArgTypes>()...)

trong đó INVOKE (f, t1, t2, ..., tN) được định nghĩa như static_cast<void>(INVOKE(f, t1, t2, ..., tN))thể R có thể đủ điều kiện cv void, nếu không thì INVOKE (f, t1, t2, ..., tN) , được chuyển đổi hoàn toàn thành R

Lưu ý rằng lambda thứ 2 trở lại bool, đối với std::function<void(int)>, như được hiển thị ở trên, static_cast<void>(INVOKE(f, t1, t2, ..., tN))là một biểu thức hợp lệ (trả lại boolchỉ được chuyển đổi thành void). Sau đó, nó cũng có thể chuyển đổi thành std::function<void(int)>ngầm định và gây ra vấn đề mơ hồ.


6

Bạn có thể rõ ràng static_castlambda đến loại thích hợp

using FunBoolRet = std::function<bool(int)>;

do_some(static_cast<FunBoolRet >([&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }));

Hoặc lưu trữ lambda vào std::function<bool(int)>loại thích hợp và chuyển đến hàm (nếu do_some(lmda)nên được gọi nhiều lần)

FunBoolRet lmda = [&](int in)
{
    local_to_be_modified += in;
    std::cout << "This is bool-" << std::endl;
    return true;
};    
do_some(lmda); // pass the lambda

Hoặc như @MaxLanghof đề xuất đơn giản là xây dựng std::function<bool(int)>từ lambda khi đang di chuyển

do_some(FunBoolRet{
   [&](int in) 
   {
      local_to_be_modified += in;
      std::cout << "This is bool-" << std::endl;
      return true;
   }
});

Bạn có thể bỏ qua static_castvà chỉ cần xây dựng một std::functiontrực tiếp từ nó. Đó là tất cả những gì xảy ra trong quá trình chuyển đổi ngầm.
Max Langhof

Quan điểm của tôi là bạn có thể loại bỏ static_cast<và cuối cùng theo nghĩa đen >và nó sẽ làm điều tương tự nhưng ít gõ hơn. Nó không cần nhiều dòng hoặc bất cứ điều gì. godbolt.org/z/fQTqF4
Max Langhof
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.