C++使用lambda进行隐式构造函数调用,期望函数指针

DrL*_*ig3 5 c++ lambda language-lawyer c++11 c++14

我试图从lambda函数隐式构造一个对象.对象的构造函数将函数指针作为参数.但是代码[1]不能使用以下消息进行编译:

6 : <source>:6:5: note: candidate constructor not viable: no known conversion from '(lambda at /tmp/compiler-explorer-compiler117117-54-dfxyju.lkw98/example.cpp:22:14)' to 'Bar' (aka 'bool (*)()') for 1st argument
    Foo(Bar b) : m_b{b} {}
Run Code Online (Sandbox Code Playgroud)

但是标准规定lambda函数可以隐式转换为具有相同参数和返回类型的函数指针[2].这应该适用于此,因此我希望构造函数可以调用.

那么代码为什么不编译呢?谢谢你的解释!


[1]代码示例:

using Bar = bool(*)();

class Foo
{
public:
    Foo(Bar b) : m_b{b} {}
private:
    Bar m_b;
};

int main()
{   
    // working
    Foo f1 ( [](){ return true; });
    Foo f2 = Bar( [](){ return true; });

    // working implicit conversion
    bool(*tmp)() = []() { return true; };
    Foo f3 = tmp;

    // not working
    Foo f4 = [](){ return true; };

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/g/QE4v1Z


[2] C++ 14标准在5.1.2节中指出:

没有lambda-capture的lambda表达式的闭包类型有一个公共的非虚拟非显式const转换函数,用于指向具有与闭包类型的函数调用操作符相同的参数和返回类型的函数.此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用运算符具有相同的效果.

这意味着lambda应该隐式(非显式)可转换为函数指针.


测试用:

  • Clang5.0.0 -std = c ++ 14
  • MSVC14.12 /许可 -

son*_*yao 14

是的,它可以隐式转换为函数指针; 这就是为什么bool(*tmp)() = []() { return true; };有效.关键是在一个隐式转换序列中,只允许一个用户定义的转换.

隐式转换序列按以下顺序包含以下内容:

  1. 零个或一个标准转换序列;
  2. 零或一个用户定义的转换;
  3. 零个或一个标准转换序列.

在考虑构造函数或用户定义的转换函数的参数时,只允许一个标准转换序列(否则可以有效地链接用户定义的转换).

Foo f4 = [](){ return true; };,这是复制的初始化时,拉姆达必须被转换到通过拉姆达的用户定义的转换函数的函数指针,然后转换成Foo经由所述转换构造Foo,两个用户定义的转换是必需的,但不允许的.

BTW:

  1. Foo f1 ( [](){ return true; });因为直接初始化Foo将直接调用构造函数; lambda被转换为函数指针,然后作为参数传递给构造函数,这很好.

  2. Foo f2 = Bar( [](){ return true; });是因为lambda显式转换为函数指针,Foo后来将其隐式转换为函数指针.

  3. bool(*tmp)() = []() { return true; }; Foo f3 = tmp;因为lambda被隐式转换为函数指针tmp,然后tmp被转换Foo; 隐式转换序列只需要一个用户定义的转换,那就没问题了.

  4. Foo f5 = +[](){ return true; };作品因为operator+导致拉姆达转化为函数指针,这意味着对+[](){ return true; }你会得到类型的函数指针bool(*)(),那么这个故事是一样的f2.