【问题标题】:Is this a forwarding reference?这是转发参考吗?
【发布时间】:2017-04-10 16:26:44
【问题描述】:

在 Scott Meyers 的这个例子中,右值引用和转发引用之间的区别已经很清楚了:

Widget&& var1 = someWidget;     // here, “&&” means rvalue reference (1)

auto&& var2 = var1;             // here, “&&” does not mean rvalue reference (2)

template<typename T>
void f(std::vector<T>&& param); // here, “&&” means rvalue reference (3)

template<typename T>
void f(T&& param);              // here, “&&”does not mean rvalue reference (4)

本质上,当我们有一个可演绎的上下文时,就会发生区别,因此案例 (3) 明确指出我们有一个 vector&lt;...&gt;&amp;&amp;,而案例 (4) 中的 T 将被推导出并(应用参考折叠规则后)按“价值类别”分类。

但是稍微复杂一点的模式匹配会发生什么?以下面的例子为例:

template <template <class...> class Tuple, class... Ts>
void f(Tuple<Ts...>&& arg)
{

}

&amp;&amp; 在这里是什么意思?

【问题讨论】:

  • 可演绎的上下文并不重要。 (3) 和 (4) 都是可演绎的。

标签: c++ c++11 perfect-forwarding forwarding-reference


【解决方案1】:

在您的最后一个示例中,arg 是一个右值引用。

转发引用是对 cv 非限定模板参数的右值引用

Tuple&lt;Ts...&gt; 不是模板参数。

(来自 [temp.deduct.call] 的引用。)

【讨论】:

  • 如果我有Tuple&lt;Ts&amp;&amp;...&gt;Ts 的行为是否类似于转发引用,或者我们只是将 && 附加到每个参数并适用典型的引用折叠?
  • Lorah Attkins 你说的没什么区别。将 && 附加到参数和发生引用折叠是转发引用工作的机制。关键问题是如何推导类型,我认为这里的类型推导不会使其成为转发引用。
【解决方案2】:

它是一个右值引用,而不是一个转发引用。

最简单的确定方法是尝试传递一个左值,如果失败,则为右值引用,否则为转发引用:

template<typename... Ts>
struct foo {};

//f function definition

int main() {
    foo<int, double> bar;
    f(bar); // fails! Cannot bind lvalue to rvalue reference
    f(foo<int, double>{}); // ok, rvalue is passed
}

【讨论】:

    【解决方案3】:

    概念转发引用不是一个标准的概念,看到它是有用的,但如果你想正确理解和处理它,你必须理解引用算法。 (我相信迈耶的书中也有关于它的章节)

    转发引用的概念背后是引用算法:

    • && && = &&
    • && & = &
    • & && = &
    • & & = &

    让我们用转发引用模拟编译器模板类型推导

    template<class T>
    void foo(T&&);
    //...
    const int i=42;
    foo(i); // the compiler will defines T = const int &
             //          T&&  = const int & && = const int &
             // => the compiler instantiates void foo<const int &>(const int &);
    foo(6*7);// the compiler will defines T = int
             //          T&&  = int &&
             // the compiler instantiates  void foo<int>(int &&);
    

    在这种情况下,模板 foo 的实例化可以产生 通过左值引用获取参数的函数或接受参数右值引用的函数:转发引用是 右值引用或左值引用,取决于模板类型推导。之所以这样命名,是因为在这种情况下,参数要么作为左值传递,要么作为xvalue传递,这就是T&amp;&amp; std::forward&lt;T&gt;(T&amp;&amp; a)的工作

    如果你声明一个函数有:

     template<class T>
     void foo(ATemplateClass<T> && a);
    

    无论编译器为 T 推导出什么类型,您都会得到一个右值引用参数。

    【讨论】:

    • 您确定您的样品吗?我相信 foo(42) 和 foo(6*7) 在这里是在同一条船上:godbolt.org/g/jOEK9b
    • @RTempete 帖子的那部分似乎是错误的:如您所见,两者都推断为int&amp;&amp; - 相同,而不是const。如果 Oliv 能解释为什么他们认为要么应该是 const,和/或应该有一些差异,基于是否涉及(微不足道,即使在 -O0 处优化)表达式,那将更有用.
    • 对不起,示例代码中有两个错误。 f(42)-> 右值引用和 f(6*7) 也是。
    猜你喜欢
    • 1970-01-01
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-11
    • 2020-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多