【问题标题】:Passing lambda, which returns polymorphic unique_ptr, as function pointer将返回多态 unique_ptr 的 lambda 作为函数指针传递
【发布时间】:2016-08-25 13:37:07
【问题描述】:

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

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

  • 为什么我要明确声明返回类型?
  • 为什么它适用于没有这种额外返回类型的std::function

#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

【问题讨论】:

  • std::add_pointer_t&lt;std::unique_ptr&lt;Base&gt;&gt;; 不会是指向unique_ptr&lt;base&gt; 的指针吗?请参阅coliru.stacked-crooked.com/a/5a1c461bfb6199a8 我可能不是这里的专家,但我可以猜想编译器在将unique_ptr&lt;derrived&gt; 转换为指向unique_ptr&lt;base&gt; 的指针时遇到问题,同时将unique_ptr&lt;base&gt; 转换为指向unique_ptr&lt;base&gt; 的指针它可以以某种方式隐式执行
  • @Hayt 我使用std::add_pointer_t&lt;std::unique_ptr&lt;Base&gt;()&gt;;,请注意末尾的( )
  • 啊,是的。那没关系。它可能必须对函数 ptr (第一个是)和带有一些转换的函数对象的不同行为做一些事情,但我现在找不到任何链接(与 lambdas 组合。可能也可以将 lambda 转换为 c 函数 ptr)。

标签: c++ lambda function-pointers c++14


【解决方案1】:

TL;DR;

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

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

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

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

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

当你显式指定 lambda 的返回类型时,你改变了 lambda 的调用运算符的返回类型(实际上是与之关联的闭包类型,见最后的引用),所以转换是可能的。


另一方面,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 的标准引用(重点是我的):

不带 lambda 捕获的非泛型 lambda 表达式的闭包类型有一个转换函数,指向具有 相同参数和返回类型的 C++ 语言链接 (7.5) 的函数的指针 作为 闭包类型的函数调用运算符。 [...]

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

如果表达式INVOKE (f, declval&lt;ArgTypes&gt;()..., R)(被认为是未计算的操作数(第 5 条))格式正确(20.12.2),则 F 类型的可调用对象 f 对于参数类型 ArgTypes 和返回类型 R 是可调用的。

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

1 定义INVOKE (f, t1, t2, ..., tN)如下:

(1.x) - [...]

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

2 定义INVOKE (f, t1, t2, ..., tN, R)为static_cast(INVOKE (f, t1, t2, ..., tN))如果R是cv void,否则INVOKE (f, t1, t2, ..., tN)隐式转换为R

【讨论】:

    【解决方案2】:

    除了 Holt 的回答,并涵盖您的第一个问题:您不必将返回类型显式指定为尾随返回类型。但是由于您正在创建一个unique_ptr&lt;Derived&gt;,但想要一个unique_ptr&lt;Base&gt;,您应该返回后者并在您的函数中执行转换。

    所以,我相信,这就是

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

    也可以编译。

    【讨论】:

    • @m.s.这里没有例外的安全问题(但我同意一般来说你应该选择make_unique),但如果你需要你可以std::unique_ptr&lt;Base&gt;(std::make_unique&lt;Derived&gt;())(这基本上是f3 隐含的,或者std::function 版本) - 但如果需要转换为函数指针,最好的方法是显式指定返回类型,如f3
    猜你喜欢
    • 1970-01-01
    • 2021-10-05
    • 2013-04-28
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 2011-02-28
    • 1970-01-01
    • 2018-09-23
    相关资源
    最近更新 更多