C++ 将lambda作为函数指针传递,该函数返回多态唯一\u ptr

C++ 将lambda作为函数指针传递,该函数返回多态唯一\u ptr,c++,lambda,function-pointers,c++14,C++,Lambda,Function Pointers,C++14,我想传递一个非捕获lambda,它返回一个 std::unique_ptr,作为类型为std::unique_ptr(*)的函数指针() 但是,只有当我将lambda的返回类型显式声明为std::unique\u ptr时,这才有效 为什么我要显式地声明返回类型 没有这个额外的返回类型,为什么它对std::function有效 #包括 #包括 结构基{virtual~Base()=default;}; 派生结构:基{}; 结构失败RF2 { 使用Function=std::add\u po

我想传递一个非捕获lambda,它返回一个
std::unique_ptr
,作为类型为
std::unique_ptr(*)的函数指针(

但是,只有当我将lambda的返回类型显式声明为
std::unique\u ptr
时,这才有效

  • 为什么我要显式地声明返回类型
  • 没有这个额外的返回类型,为什么它对
    std::function
    有效

#包括
#包括
结构基{virtual~Base()=default;};
派生结构:基{};
结构失败RF2
{
使用Function=std::add\u pointer\t;
FailsForF2(函数f){}
};
结构工程
{
使用Function=std::Function;
作品(功能f){}
};
std::unique_ptr fun(){return std::make_unique();}
int main()
{
自动f1=[](){return std::make_unique();};
自动f2=[](){return std::make_unique();};
自动f3=[]()->std::unique_ptr{return std::make_unique();};
工程x1(f1);
工程x2(f2);
工程x3(f3);
f2 x4(f1)的故障;
故障f2 x5(f2);
失效f2 x6(f3);
}
gcc错误

main.cpp: In function 'int main()':

main.cpp:34:20: error: invalid user-defined conversion from 'main()::<lambda()>' to 'FailsForF2::Function {aka std::unique_ptr<Base> (*)()}' [-fpermissive]

    FailsForF2 x5(f2);

                    ^

main.cpp:26:17: note: candidate is: main()::<lambda()>::operator std::_MakeUniq<Derived>::__single_object (*)()() const <near match>

    auto f2 = [](){return std::make_unique<Derived>();};

                 ^

main.cpp:26:17: note:   no known conversion from 'std::_MakeUniq<Derived>::__single_object (*)() {aka std::unique_ptr<Derived> (*)()}' to 'FailsForF2::Function {aka std::unique_ptr<Base> (*)()}'

main.cpp:10:4: note:   initializing argument 1 of 'FailsForF2::FailsForF2(FailsForF2::Function)'

    FailsForF2(Function f) {}
main.cpp:在函数“int main()”中:
main.cpp:34:20:错误:从“main()::”到“FailsForF2::Function{aka std::unique_ptr(*)()}”[-fppermissive]的用户定义转换无效
故障f2 x5(f2);
^
main.cpp:26:17:注意:候选对象是:main()::操作符std::\u MakeUniq::\u单个对象(*)()常量
自动f2=[](){return std::make_unique();};
^
main.cpp:26:17:注意:没有已知的从'std:_MakeUniq::u single_object(*)到'FailsForF2::Function{aka std::unique_ptr(*)的转换
main.cpp:10:4:注意:初始化'FailsForF2::FailsForF2(FailsForF2::Function)'的参数1
FailsForF2(函数f){}

TL;博士

  • FailsForF2
    失败是因为
    std::unique_ptr(*)(
    不能隐式转换为
    std::unique_ptr(*)(
  • Works
    之所以有效,是因为
    std::unique_ptr
    可以隐式转换为
    std::unique_ptr
    (请参见末尾的标准引号)

lambda可隐式转换为具有相同返回类型和参数1的函数指针,因此三个lambda可分别转换为:

std::unique_ptr<Base> (*) ()
std::unique_ptr<Derived> (*) ()
std::unique_ptr<Base> (*) ()
当您显式指定lambda的返回类型时,您将更改lambda的call运算符的返回类型(实际上是与之关联的闭包类型,请参见末尾的引号),因此可以进行转换


另一方面,
std::function
没有此类约束-std::function的构造函数是模板化的,因此它可以接受任何可调用的:

template <typename F>
std::function(F &&f);
模板
std::函数(F&&F);
…只要以下各项有效2:

调用(f,std::forward(args)…,R)
1引用N4594第5.1.5/7节的标准内容(重点是我的):

不具有lambda捕获的非一般lambda表达式的闭包类型具有与C++语言链接(7.5)具有强>相同参数和返回类型< /强>的函数指针的转换函数 闭包类型的函数调用运算符。[……]

2 N4594的标准引用,§20.12.12.2/2:

如果表达式
INVOKE(f,declval(),R)
被视为未赋值的操作数(第5条),则类型为f的可调用对象f可用于参数类型ArgTypes和返回类型R(20.12.2)

…和§20.12.2(重点是我的,1.1到1.6是关于指向成员函数的指针(或类似的),因此在这里不相关):

1定义
调用(f,t1,t2,…,tN)
如下:

(1.x)-[…]

(1.7)f(t1,t2,…,tN)在所有其他情况下

2将
调用(f,t1,t2,…,tN,R)
定义为静态强制转换(
调用(f,t1,t2,…,tN)
),如果R为cv void,否则
调用(f,t1,t2,…,tN)
隐式转换为R


除了Holt的答案之外,为了回答您的第一个问题:您不必显式地将返回类型指定为尾部返回类型。但是,由于您正在创建一个
唯一的\u ptr
,但需要一个
唯一的\u ptr
,因此您应该返回后者并在函数中执行转换

所以,我相信,这是一种

auto f2 = [](){ return std::unique_ptr<Base>{new Derived()}; };
auto f2=[](){return std::unique_ptr{new Derived()};};

它也可以编译。

不会
std::add\u pointer\t是指向
唯一\u ptr
的指针吗?请看,我可能不是这里的专家,但我可以猜到编译器在将
唯一\u ptr
转换为指向
唯一\u ptr
的指针时遇到问题,而将
唯一\u ptr
转换为指向
唯一\u ptr
的指针时可能会遇到问题implicitly@Hayt我使用
std::add\u pointer\t请注意右边的
()
。那没关系。它可能与函数ptr(第一个是)和函数对象的不同行为有关,但我现在找不到任何与之相关的链接(与lambdas的组合有关。可能还有lambda到c函数ptr的转换)@m.s.这里没有例外的安全问题(但我同意通常你应该选择
make_unique
),但是如果你需要,你可以
std::unique\u ptr(std::make_unique())
(这基本上是
f3
隐含的功能,或者
std::function
版本)-但是如果你需要转换成函数指针,最好的方法是显式指定返回类型,如
f3
中所示。
template <typename F>
std::function(F &&f);
INVOKE(f, std::forward<Args>(args)..., R)
auto f2 = [](){ return std::unique_ptr<Base>{new Derived()}; };