将 returns 多态 unique_ptr 的 lambda 作为函数指针传递

Passing lambda, which returns polymorphic unique_ptr, as function pointer

我想传递一个非捕获的 lambda,return std::unique_ptr<Derived>,作为 std::unique_ptr<Base>(*)().

类型的函数指针

但是,这仅在我将 lambda 的 return 类型明确声明为 std::unique_ptr<Base> 时才有效。


#include <functional>
#include <memory>

struct Base{virtual ~Base()=default;};
struct Derived : Base{};

struct FailsForF2
{
   using Function = std::add_pointer_t<std::unique_ptr<Base>()>;
   FailsForF2(Function f) {}
};

struct Works
{
   using Function = std::function<std::unique_ptr<Base>()>;
   Works(Function f) {}
};


std::unique_ptr<Derived> fun() {return std::make_unique<Derived>();} 

int main()
{

   auto f1 = [](){return std::make_unique<Base>();};
   auto f2 = [](){return std::make_unique<Derived>();};
   auto f3 = []()->std::unique_ptr<Base>{return std::make_unique<Derived>();};

   Works x1(f1);
   Works x2(f2);
   Works x3(f3);

   FailsForF2 x4(f1);
   FailsForF2 x5(f2);
   FailsForF2 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) {}

live example

TL;DR;

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

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

std::unique_ptr<Base> (*) ()
std::unique_ptr<Derived> (*) ()
std::unique_ptr<Base> (*) ()

由于 std::unique_ptr<Derived> (*) () 不同于(且不可转换为)std::unique_ptr<Base> (*) ()FailsForF2 构造函数没有可行的重载。请参阅以下代码:

std::unique_ptr<Derived> (*pfd) () = f2; // Compiles.
std::unique_ptr<Base> (*pfb) () = pfd;   // Does not compile (invalid conversion).

当您显式指定 lambda 的 return 类型时,您更改了 lambda 的调用运算符的 return 类型(实际上与其关联的闭包类型,请参阅引号结束), 所以转换是可能的。


另一方面,

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

template <typename F>
std::function(F &&f);

...只要以下有效2:

INVOKE(f, std::forward<Args>(args)..., R)

1 N4594 的标准引述,§5.1.5/7(重点是我的):

The closure type for a non-generic lambda-expression with no lambda-capture has a conversion function to pointer to function with C++ language linkage (7.5) having the same parameter and return types as the closure type’s function call operator. [...]

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

A callable object f of type F is Callable for argument types ArgTypes and return type R if the expression INVOKE (f, declval<ArgTypes>()..., R), considered as an unevaluated operand (Clause 5), is well formed (20.12.2).

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

1 Define INVOKE (f, t1, t2, ..., tN) as follows:

(1.x) - [...]

(1.7) - f(t1, t2, ..., tN) in all other cases.

2 Define INVOKE (f, t1, t2, ..., tN, R) as static_cast(INVOKE (f, t1, t2, ..., tN)) if R is cv void, otherwise INVOKE (f, t1, t2, ..., tN) implicitly converted to R.

除了 Holt 的回答之外,还要回答您的第一个问题:您不必将 return 类型明确指定为尾随 return 类型。但是由于您正在创建一个 unique_ptr<Derived>,但想要一个 unique_ptr<Base>,您应该 return 后者并在您的函数中执行转换。

所以,我相信,这与

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

这也编译。