将 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>
时才有效。
- 为什么我要明确说明 return 类型?
- 为什么没有这种额外的 return 类型,它对
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) {}
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()}; };
这也编译。
我想传递一个非捕获的 lambda,return
std::unique_ptr<Derived>
,作为 std::unique_ptr<Base>(*)()
.
但是,这仅在我将 lambda 的 return 类型明确声明为 std::unique_ptr<Base>
时才有效。
- 为什么我要明确说明 return 类型?
- 为什么没有这种额外的 return 类型,它对
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) {}
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, otherwiseINVOKE (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()}; };
这也编译。