【问题标题】:Why can't lvalue references bind to const "forwarding references"?为什么左值引用不能绑定到 const “转发引用”?
【发布时间】:2021-12-25 02:18:59
【问题描述】:

“转发引用”用引号引起来,因为const-qualified 转发引用实际上并不是转发引用,但我想明确表示我特别指的是函数模板。 p>

采取以下测试功能(代码重复以避免:

#include <iostream>
#include <type_traits>
#include <typeinfo>

using namespace std;

template <typename T, typename U> void print_types() {
  cout << (is_const_v<remove_reference_t<T>> ? "const " : "")
       << typeid(T).name()
       << (is_rvalue_reference_v<T>   ? " &&"
           : is_lvalue_reference_v<T> ? " &"
                                      : "")
       << ", " << (is_const_v<remove_reference_t<U>> ? "const " : "")
       << typeid(U).name()
       << (is_rvalue_reference_v<U>   ? " &&"
           : is_lvalue_reference_v<U> ? " &"
                                      : "")
       << endl;
}

template <typename T> void print_rvalue_reference(T &&t) {
  print_types<T, decltype(t)>();
}

template <typename T> void print_const_rvalue_reference(const T &&t) {
  print_types<T, decltype(t)>();
}

int main() {
  int i = 1;
  const int j = 1;

  print_rvalue_reference(1); // int, int &&
  print_rvalue_reference(i); // int &, int &
  print_rvalue_reference(j); // const int &, const int &

  print_const_rvalue_reference(1); // int, const int &&
  print_const_rvalue_reference(i); // error
  print_const_rvalue_reference(j); // error
}

首先,我想指出 print_rvalue_reference(j) 之所以有效,是因为 print_rvalue_reference 从不在非 const 上下文中使用 t。如果不是这种情况,模板实例化就会失败。

我很困惑为什么main 中的最后两个调用会在编译期间导致错误。引用折叠允许using T = int &amp;; const T &amp;&amp; 变为int &amp;using T = const int &amp;; const T &amp;&amp; 变为const int &amp;,这意味着print_const_rvalue_reference&lt;int &amp;&gt;(i)print_const_rvalue_reference&lt;const int &amp;&gt;(j) 是有效的。

print_rvalue_reference(j)确实推导出T是const int &amp;时,为什么print_const_rvalue_reference(j)推​​导出T是int而不是const int &amp;? (真实的)转发引用是否特例以产生int &amp; 而不是int 作为其推导指南的一部分?

【问题讨论】:

  • template &lt;typename T&gt; void print_const_rvalue_reference(const T &amp;&amp;t) T 不是转发/通用引用,只能绑定到一个 R 值
  • @doug 正如我在第一段中所述,我知道const T &amp;&amp; 不是转发参考。但是,您提到的函数可以绑定到左值:int i; print_const_rvalue_reference&lt;int &amp;&gt;(i); 有效,print_const_rvalue_reference&lt;const int &amp;&gt;(i) 也有效。
  • print_const_rvalue_reference(const T &amp;&amp; i); 只能绑定到 R 值。例如,这将起作用:print_const_rvalue_reference(+i);,因为一元运算符 + 使表达式 +i 成为 R 值。
  • @doug 我只是给出了两个函数绑定到左值的例子......
  • 强调了我的一点小烦恼:我们经常听到通用/转发引用因为引用折叠而起作用。这是真的,但另一点,至少同样重要,使它们起作用的是模板参数推导有其特殊情况规则,专门用于具有左值参数的转发引用。

标签: c++ c++11 templates


【解决方案1】:

实际的转发引用确实是推导引用类型的特殊情况。见[temp.deduct.call]/3

如果 P 是 cv 限定类型,则在类型推导时忽略 P 类型的顶级 cv 限定符。如果P是引用类型,则使用P所引用的类型进行类型推导。 ... 如果P 是转发引用且参数是左值,则使用类型“A 的左值引用”代替A 进行类型推导。

因此,在Pconst T&amp;&amp;(不是转发引用)的情况下,它被转换为const T 并且参数是否为左值不影响类型推导,因为值类别不是参数类型的一部分。相反,演绎过程只是尝试查找T 使得const T 与参数的类型相同(可能带有额外的cv 限定)。在ij的情况下,这意味着T被推导出为int,参数类型为const int&amp;&amp;

但在特殊的转发引用情况下,虽然P仍然从T&amp;&amp;转换为T,但它原本是转发引用的事实导致将参数类型替换为对应的左值引用类型(如果参数是左值)。因此,在i 的情况下,转换后的参数类型是int&amp;,在j 的情况下,它是const int&amp;。在这两种情况下,T 的推导方式使其与转换后的参数类型相同,这意味着 T 被推导为引用类型。

【讨论】:

    猜你喜欢
    • 2017-04-15
    • 1970-01-01
    • 1970-01-01
    • 2016-06-09
    • 2017-04-13
    • 1970-01-01
    • 2021-12-22
    • 2018-01-13
    • 1970-01-01
    相关资源
    最近更新 更多