【问题标题】:Why is T const&& not a forwarding reference?为什么 T const&& 不是转发参考?
【发布时间】:2017-01-11 05:38:40
【问题描述】:

在模板的上下文中,应用了以下“引用折叠”规则:

template <typename T>
void foo(T && t)
{
    //T&  &   -> T&
    //T&  &&  -> T&
    //T&& &   -> T&
    //T&& &&  -> T&&
}

为什么该语言禁止“通用引用”使用const 限定符?

template <typename T>
void foo(T const && t)

如果类型已解析为引用(4 个案例中的 3 个),这似乎是有意义的。

我确信这个想法与该语言的其他一些设计方面不兼容,但我无法看到完整的画面。

【问题讨论】:

  • 你为什么不直接使用const T&amp;
  • 我怀疑const&amp;&amp;const&amp; 没有什么不同。临时变量(r 值)已经可以绑定到 const&amp; 那么const&amp;&amp; 可以添加什么?
  • AFAIK,这已经发生在参考文献中;如果您的TU const &amp;,则T &amp;&amp; 将折叠为U const &amp;。术语“通用引用”确实意味着 通用 引用:您无需在其中指定 const 即可获得常量引用。
  • 可能是因为移动意味着源对象的某种“破坏”(即作为 T&amp;&amp; 传递的),您不能破坏任何 const 对象。
  • 顺便说一句,我不确定它如何与参考折叠交互,但const T&amp;&amp; is actually legal, and the standard library even makes use of it

标签: c++ templates constants move-semantics forwarding-reference


【解决方案1】:

最初的右值引用提案说,如果P 是“右值引用类型”,就会发生转换。 However, a defect report后来注意到了

另外,考虑这种情况:

template <class T> void f(const T&&);
...
int i;
f(i);

如果我们在这种情况下将T 推断为int&amp;,则f(i) 调用f&lt;int&amp;&gt;(int&amp;),这似乎违反直觉。我们更喜欢调用f&lt;int&gt;(const int&amp;&amp;)。因此,我们希望澄清措辞,即 14.8.2.1 [temp.deduct.call] 第 3 段中的A&amp; 扣除规则仅适用于T&amp;&amp; 形式,而不适用于cv T&amp;&amp;,正如注释当前所暗示的那样。

似乎有一段时间将const T &amp;&amp;TU&amp;)转换为const U&amp;。这已更改为与另一条规则一致,即const T,其中TU&amp; 将保持U&amp;(忽略引用上的cv 限定符)。因此,当您将上面示例中的T 推导出为int&amp; 时,函数参数将保持int&amp; const int&amp;

在缺陷报告中,报告者声明“我们更喜欢调用f&lt;int&gt;(const int&amp;&amp;)”,但在缺陷报告中没有提供任何理由。我可以想象,原因是在不引入与其他规则不一致的情况下解决这个问题似乎太复杂了。

我们还应该记住,缺陷报告是在右值引用仍然可以绑定到左值的时候生成的——即const int&amp;&amp; 可以绑定到一个 int 左值。直到后来,当 Dave & Doug 的一篇论文“RValue References 的安全问题”出现时,这才被禁止。因此,在我看来,(当时)有效的推论比仅仅违反直觉并放弃限定词的推论更有价值。

【讨论】:

    【解决方案2】:

    引用确实已经发生了这种情况;如果您的TU const &amp;,则T &amp;&amp; 将折叠为U const &amp;。术语“通用引用”确实意味着通用引用:您无需在其中指定 const 即可获得常量引用。

    如果你想拥有一个真正通用的引用机制,你需要你的T &amp;&amp;能够成为各种引用,将各种常量。而且,T &amp;&amp; 正是这样做的。它折叠到所有四种情况:左值和右值引用,const 和非const

    换一种说法,constness 是类型的属性,而不是引用,即当您说T const &amp; 时,实际上是在谈论U &amp;,其中UT const&amp;&amp; 也是如此(尽管对 const 的 r 值引用不太有用)。

    这意味着,如果您希望通用引用折叠到 U const &amp;,只需传递您想要的类型的东西:U const &amp;,它就会完全折叠到那个。

    更直接地回答您的问题:语言本身并不“禁止”在通用引用声明中使用const。就是说,如果你稍微改变声明通用引用的机制——即使在T&amp;&amp; 之间插入一个低级的const——那么你将不会有一个(字面上的)“通用” " 不再引用,因为它不会接受任何东西。

    【讨论】:

      【解决方案3】:

      为什么你认为该语言不允许 const r-value 引用?

      在下面的代码中,会打印什么?

      #include <iostream>
      
      struct Foo 
      {
        void bar() const & 
        {
          std::cout << "&\n";
        }
      
        void bar() const &&
        {
          std::cout << "&&\n";
        }
      };
      
      const Foo make() { 
        return Foo{}; 
      }
      
      int main()
      {
        make().bar();
      }
      

      答案:

      &&
      

      为什么?因为 make() 返回一个 const 对象,在这个上下文中它是一个临时对象。因此 r 值引用 const。

      【讨论】:

      • OP 问题措辞不佳,他在问为什么 codetemplate&lt;typename T&gt; void f(T const &amp;&amp;) 不允许 T 推断引用类型(即没有转发引用行为)
      【解决方案4】:

      Template argument deduction 具有“对 cv 非限定模板参数的右值引用”的特殊情况。转发/通用引用所依赖的正是这种非常特殊的情况。有关详细信息,请参阅链接文章中的“从函数调用中推导”部分。

      请注意,在模板参数推导之前,所有顶级 cv 限定符都被删除;但是,引用永远不会有顶级 cv 限定符,并且上述规则不适用,因此特殊规则也不适用。 (相对于指针,没有“const reference”,只有“reference to const”)

      【讨论】:

      • 没有顶级const的引用,所以你的解释很难理解
      • 有 const 指针之类的东西,所以不知道类比是什么。此外,“所有顶级 cv 限定符都被删除”仅适用于推导与非引用参数对应的参数类型。 (所以,与这个问题无关)
      • 有 const 指针,就像有 const int 一样!并且“所有顶级 cv 限定符都被删除”不适用于引用,因为引用没有顶级 cv 限定符。至少,我不知道还有什么其他原因。
      • 是的,我刚才说有 const 指针,所以你的类比不起作用
      猜你喜欢
      • 2020-12-13
      • 2019-06-26
      • 1970-01-01
      • 2012-09-04
      • 2012-08-02
      • 1970-01-01
      • 2010-11-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多