【问题标题】:Perfect forwarding in a lambda?lambda 中的完美转发?
【发布时间】:2017-08-05 13:39:29
【问题描述】:

用一个函数,可以写:

template <class T> void f(T&& x) {myfunction(std::forward<T>(x));}

但是对于 lambda,我们没有 T

auto f = [](auto&& x){myfunction(std::forward</*?*/>(x));}

如何在 lambda 中进行完美转发? decltype(x) 是否作为 std::forward 中的类型工作?

【问题讨论】:

标签: c++ lambda c++14 decltype forwarding-reference


【解决方案1】:

转发绑定到转发引用的 lambda 参数的规范方法确实是使用 decltype

auto f = [](auto&& x){
  myfunction(std::forward<decltype(x)>(x));
} //                      ^^^^^^^^^^^

【讨论】:

  • 有一个proposal for C++2a 可以使带有呼叫运算符模板的 lambda 更容易编写。
  • @Nik-Lz:你可以制作一个包:[](auto&amp;&amp;... xs)。然后展开。
  • 我可能错了,但据我了解,decltype(x) 与底层(隐藏)模板参数不同,因为 x 本身始终是左值。因此,std::forward&lt;decltype(x)&gt;(x) 永远不会因此产生纯右值。
  • @KaiPetzke:这似乎极不可能,因为forward 的目的是做一些无法推断的事情(我的意思是,从值推断,而不是从词位推断),因此需要明确的信息。您当然可以编写宏(许多库都可以),但我认为这不是您的意思。但如果你这样做了,那么我想一旦我们有了反射,就会在某个时候出现基于反射的解决方案。
  • 等等,给定函数参数int&amp;&amp; xdecltype(x) 不是左值类型吗?
【解决方案2】:

我最喜欢的成语是:

auto f = [](auto&& x){myfunction(decltype(x)(x));}

我将其读作“x,因为类型 x 被声明为”。

要了解其工作原理,请检查当 xint&amp;&amp; 时会发生什么。 decltype(x)(x)(int&amp;&amp;)(x),它产生一个对x 的右值引用。如果xint&amp;,那么我们得到(int&amp;)(x),它是对引用的noop 转换。请记住,decltype(x) 包含参考类别。

现在,对于 auto&amp;&amp; 参数,这更短但等效于:

auto f = [](auto&& x){myfunction(std::forward<decltype(x)>(x));}

另一种选择。

对于auto参数:

auto f = [](auto x){myfunction(decltype(x)(x));}

它会产生一个额外的副本,而

auto f = [](auto x){myfunction(std::forward<decltype(x)>(x));}

而是从x移动。

虽然我通常认为 C 风格的强制转换过于危险,但如果 x 不是 auto&amp;&amp; 变量,decltype(x)(x) 最多可以生成 x 的类型正确副本。为了简洁起见,有一些话要说。

【讨论】:

  • decltype cast 是一个巧妙的技巧,但我喜欢std::forward 的地方在于它创建了一个可读(且可greppable)的提示,表明转发正在进行并且附近的&amp;&amp; 是一个转发参考。
  • 我同意那个;简洁是以意义为代价的,IMO。不错的技巧,+1!
  • 您能否详细说明复制的示例?对于auto 参数:...例如int a; f(a); -> auto == int ,当auto x 初始化时第一次复制(可能优化出来),然后在第一种情况下:复制因为int(x)(肯定优化出来)在第二种情况下:移动(如果myFunction是这样实现的)因为 std::forward 强制转换为int&amp;&amp;。对吗?
  • @Gabriel 移动复制int 是无趣的。使用struct noisy{ noisy(noisy&amp;&amp;){std::cout &lt;&lt; "&amp;&amp;";} /* etc */}。移动语义对分析原始类型没有用处。除了不同的地址之外,复制或移动它们的副作用为零,因此除非您获取地址,否则移动和复制不存在,或者出于任何原因您必须遵守调用约定。
  • 对,应该在我的示例中替换它。
【解决方案3】:

使用 C++20,您现在可以为 lambda 指定模板参数列表。以下直接取自https://en.cppreference.com/w/cpp/language/lambda

auto f = []<typename ...Ts>(Ts&& ...ts) {
    return foo(std::forward<Ts>(ts)...);
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    • 2015-10-03
    • 2015-01-06
    • 2014-08-23
    • 2020-12-04
    相关资源
    最近更新 更多