lambdas的参数和返回值的类型转换规则是什么?

and*_*ras 21 c++ lambda type-conversion c++11

我最近惊讶于lambdas可以被分配给std::function具有略微不同签名的s .略有不同意味着当function指定返回时void,lambda的返回值可能会被忽略,或者参数可能是functionlambda 中的值但是引用.

请参阅此示例(ideone),其中我强调了我怀疑不兼容的内容.我认为返回值不是问题,因为你总是可以调用一个函数并忽略返回值,但是从引用到值的转换看起来很奇怪:

int main() {
    function<void(const int& i)> f;
    //       ^^^^ ^^^^^    ^
    f = [](int i) -> int { cout<<i<<endl; return i; };
    //     ^^^    ^^^^^^
    f(2);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

小问题是:为什么这段代码编译和工作?主要问题是:与lambda参数和返回值一起使用时,类型转换的一般规则是什么std::function

eca*_*mur 16

当lambda 对于该签名是Lvalue-Callable时,可以将lambda分配给类型的函数对象.反过来,Lvalue-Callable是根据INVOKE操作定义的,这意味着lambda在它是一个左值时必须是可调用的,并且它的所有参数都是所需类型和值类别的值(就好像每个参数都是调用带有该参数类型作为其签名中的返回类型的nullary函数的结果.std::function<R(ArgTypes...)>

也就是说,如果你给你的lambda一个id,以便我们可以参考它的类型,

auto l = [](int i) -> int { cout<<i<<endl; return i; };
Run Code Online (Sandbox Code Playgroud)

将其分配给a function<void(const int&)>,表达式

static_cast<void>(std::declval<decltype(l)&>()(std::declval<const int&>()))
Run Code Online (Sandbox Code Playgroud)

必须是良好的形式.

结果std::declval<const int&>()是左值引用const int,但是没有问题将它绑定到int参数,因为这只是左值到右值的转换,它被认为是出于重载解析目的的完全匹配:

return l(static_cast<int const&>(int{}));
Run Code Online (Sandbox Code Playgroud)

正如您所观察到的,如果函数对象签名具有返回类型void,则会丢弃返回值; 否则,返回类型必须是可隐式转换的.正如Jonathan Wakely指出的那样,C++ 11在这方面的行为并不令人满意(使用`std :: function <void(...)>`来调用非void函数 ; 调用std :: function <void是不合法的(Args ...)>在标准下?),但是在LWG 2420中已经修复了; 该决议适用于C++ 14的出版后修复.即使在C++ 11模式下,大多数现代C++编译器都将提供C++ 14行为(修改后)作为扩展.