如何在C++ 14中编写通用转发lambda?

mav*_*vam 15 c++ lambda perfect-forwarding c++14

如何在C++ 14中编写通用转发lambda?

试试#1

[](auto&& x) { return x; }
Run Code Online (Sandbox Code Playgroud)

在函数体内部,x是一个左值,所以这不起作用.

试试#2

[](auto&& x) { return std::forward<decltype(x)>(x); }
Run Code Online (Sandbox Code Playgroud)

这正确地转发了lambda中的引用,但它总是按值返回(除非编译器省略了副本).

试试#3

[](auto&& x) -> decltype(x) { return std::forward<decltype(x)>(x); }
Run Code Online (Sandbox Code Playgroud)

这将返回与参数相同的类型(可能-> auto&&也会起作用)并且似乎正常工作.

试试#4

[](auto&& x) noexcept -> decltype(x) { return std::forward<decltype(x)>(x); }
Run Code Online (Sandbox Code Playgroud)

添加是否noexcept使这个lambda更适用,因此严格优于#3?

Dav*_*one 7

返回类型decltype(x)不足.

通过值获取的局部变量和函数参数可以隐式移动到返回值中,但不是rvalue引用所采用的函数参数(x是左值,即使decltype(x)你通过rvalue 也是== rvalue引用).委员会给出的理由是,他们希望确定当编译器隐式移动时,没有其他人可能拥有它.这就是为什么我们可以从prvalue(临时,非引用限定返回值)和函数本地值移动.但是,有人可以做些傻事

std::string str = "Hello, world!";
f(std::move(str));
std::cout << str << '\n';
Run Code Online (Sandbox Code Playgroud)

委员会并不想默默地援引一个不安全的举动,认为他们应该从这个新的"移动"功能开始更加保守.请注意,在C++ 20中,这个问题将得到解决,你可以简单地做return x,它会做正确的事情.见http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0527r0.html

对于转发功能,我会尝试noexcept正确.这很容易,因为我们只是处理引用(它是无条件的noexcept).否则,您将破坏关注的代码noexcept.

这使得最终理想的转发lambda看起来如下:

auto lambda = [](auto && x) noexcept -> auto && { return std::forward<decltype(x)>(x); };
Run Code Online (Sandbox Code Playgroud)

返回类型decltype(auto)将在这里执行相同的操作,但auto &&更好的文档,此函数始终返回引用.它还避免再次提到变量名称,我想这使得重命名变量更容易一些.

从C++ 17开始,转发lambda在可能的情况下也隐含constexpr.


Bar*_*rry 6

前两次尝试不起作用,因为返回类型推导会丢弃顶级cv限定符和引用,这使得通用转发不可能.你的第三个是完全正确的,只是不必要的详细,转换隐含在返回类型中.与noexcept转发无关.

以下是一些值得完全抛弃的选项:

auto v0 = [](auto&& x) -> decltype(x) { return x; };
auto v1 = [](auto&& x) -> auto&& { return x; };
auto v2 = [](auto&& x) -> auto&& { return std::forward<decltype(x)>(x); };
auto v3 = [](auto&& x) -> decltype(auto) { return std::forward<decltype(x)>(x); };
Run Code Online (Sandbox Code Playgroud)

v0将编译并看起来好像它返回正确的类型,但是如果你使用rvalue引用调用它,由于从lvalue reference(x)到rvalue reference(decltype(x))的请求隐式转换,它将在编译时失败.gcc和clang都失败了.

v1将始终返回左值引用.如果你用临时的方式调用它,那就会给你一个悬空参考.

这两个v2v3是正确的,通用的转发lambda表达式.因此,对于尾随返回类型(decltype(auto),auto&&decltype(x)),您有三个选项,对于lambda的主体(调用std::forward)有一个选项.