【问题标题】:Call to lambda is ambiguous despite explicitly stating the return type尽管明确说明了返回类型,但对 lambda 的调用是模棱两可的
【发布时间】:2020-02-25 20:31:04
【问题描述】:

鉴于 lambda 的类型是可确定的(可转换为 std::function(如果我错了,请纠正我),重载函数应该同时包含两个函子。 问题是:尽管明确定义了 lambda 类型,为什么下面会出现编译错误? ([&]() -> Type {})

请注意,对于我当前的解决方案,我需要按引用捕获, 这就是代码包含它的逻辑的原因。

下面的例子描述了这个问题:

#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;
      }
   );
}

【问题讨论】:

  • 因为 std::function&lt;void(int)&gt; 甚至可以从返回某些内容的 lambda 构造(这会导致返回值被忽略)。
  • 顺便说一句,明确指定该 lambda 的返回类型完全没有任何作用。

标签: c++ c++11 lambda implicit-conversion std-function


【解决方案1】:

因为返回 bool 的第二个 lambda 表达式可以隐式转换为 std::function&lt;void(int)&gt;std::function&lt;bool(int)&gt;

std::function 有一个转换构造函数:

template< class F >
function( F f );

此构造函数不参与重载决议,除非 f 是 Callable 对于参数类型 Args... 和返回类型 R。(C++14 起)

正如Callable的定义,

以下表达式必须有效:

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

其中 INVOKE(f, t1, t2, ..., tN) 定义为 static_cast&lt;void&gt;(INVOKE(f, t1, t2, ..., tN)) 如果 R 可能 cv 限定 void,否则 INVOKE(f, t1, t2, ..., tN),隐式 转换为 R

请注意,第二个 lambda 返回 bool,对于 std::function&lt;void(int)&gt;,如上所示,static_cast&lt;void&gt;(INVOKE(f, t1, t2, ..., tN)) 是一个有效的表达式(返回的 bool 只是转换为 void)。然后它也可以隐式转换为std::function&lt;void(int)&gt; 并导致歧义问题。

【讨论】:

    【解决方案2】:

    您可以将 lambda 显式 static_cast 转换为正确的类型

    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;
       }));
    

    或者将 lambda 存储到正确的 std::function&lt;bool(int)&gt; 类型并传递给函数(如果应该多次调用 do_some(lmda)

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

    或如 @MaxLanghof 建议的那样 只需随时随地从 lambda 构造 std::function&lt;bool(int)&gt;

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

    【讨论】:

    • 你可以跳过static_cast,直接从它构造一个std::function。无论如何,这就是隐式转换期间发生的所有事情。
    • 我的意思是,你可以从字面上删除static_cast&lt; 和最后一个&gt;,它会做同样的事情,但输入更少。它不需要更多的行或任何东西。 godbolt.org/z/fQTqF4
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多