【问题标题】:C++98/03 reference collapsing and cv qualifiersC++98/03 参考折叠和 cv 限定符
【发布时间】:2013-01-23 13:41:11
【问题描述】:

下面的代码编译(gcc 4.7.2 或 icc 13)并产生“1 2”输出。这意味着 const 限定符被删除,即。例如,f<int&> 的参数类型为 int&

为什么会这样?据我了解,根据§14.3.1.4:

如果模板参数T 的模板参数将类型命名为“引用cv1 S”,则尝试创建类型“引用cv2 em> T” 创建类型“reference to cv12 S”,其中 cv12 是 cv 限定符 cv1 的并集和cv2。冗余的 cv 限定符被忽略。

const 不应被丢弃。代码如下:

#include <iostream>
using namespace std;

template <typename T>
void f(const T& t)
{
    t++;
}

int main()
{
    int a = 1;

    cout << a;
    f<int&>(a);
    cout << ' ' << a << endl;

    return 0;
}

【问题讨论】:

  • 我在 C++98 或 C++03 中都找不到该引用。 §14.3.1 是“模板类型参数”,没有小节或第 4 段。

标签: c++ reference c++03 qualifiers c++98


【解决方案1】:
当指定-std=c++98 标志时,

GCC 4.7.2 不会编译它。事实上,在 C++98(以及 C++03)中,对引用的引用不会崩溃。

尝试实例化f&lt;int&amp;&gt;,其中T = int&amp;,会产生以下函数签名(这里我有意切换参数类型Tconst 说明符的位置,这是允许的,因为const T&amp; 是同T const&amp;):

void f(int& const& t) // ERROR: reference to reference is illegal

上述内容在 C++98 中不合法,在 C++03 中也不合法。始终如一,这是您从 GCC 4.7.2 得到的错误:

Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:15:14: error: no matching function for call to 'f(int&)'
source.cpp:15:14: note: candidate is:
source.cpp:5:6: note: template<class T> void f(const T&)
source.cpp:5:6: note:   template argument deduction/substitution failed:
source.cpp: In substitution of 'template<class T> void f(const T&) [with T = int&]':
source.cpp:15:14:   required from here
source.cpp:5:6: error: forming reference to reference type 'int&'

不过,如果您使用-std=c++11 标志,则编译器会在实例化模板时执行引用折叠:对左值引用的左值引用变为左值引用:

void f(int& const& t) == void f(int& t)

这里const 限定符被删除,因为它适用于引用,而不适用于被引用的对象。由于无法重新分配引用,它们本质上是const,这就是为什么const 被认为是多余的并被删除的原因。请参阅 this Q&A on SO 了解说明。

这会产生一个对左值引用的左值引用,它解析为一个简单的左值引用。因此,右侧的签名被实例化。

以上是解决f&lt;int&amp;&gt;(a) 调用的可行候选者,因此它编译时不会出错。

【讨论】:

  • void f(const int&amp; t) { t++; } 怎么编译?它修改了t,这是一个常量引用。
  • 而不是默认c++11,上面的代码已经在gcc中作为扩展工作了一段时间(它在4.1中以默认模式编译,但如果你要求@987654340会失败@.
  • @Kleist:注释中的函数不应该编译。
  • @Kleist:我想我修正了解释。
  • @AndyProwl:是的,现在说得通了。特别是您链接中的答案stackoverflow.com/a/4494126/164277 让我很清楚。
【解决方案2】:

这里是 1770 年,有问题的引用似乎源自:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1770.html

14.3.1 - 模板类型参数

-4- 如果模板参数 T 的模板参数将类型命名为“对 cv1 S 的左值引用”,则尝试创建类型“对 cv2 T 的(左值或右值)引用”会创建类型“对 cv12 S 的左值引用”,其中 cv12 是 cv 限定符 cv1 和 cv2 的并集。如果模板参数将类型命名为“对 cv1 S 的右值引用”,则尝试创建类型“对 cv2 T 的左值引用”会创建类型“对 cv12 S 的左值引用”。如果模板参数将类型命名为“对 cv1 S 的右值引用”,则尝试创建类型“对 cv2 T 的右值引用”会创建类型“对 cv12 S 的右值引用”。冗余的 cv 限定符被忽略。

这里是 2118,引用已被删除:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html

14.3.1 - 模板类型参数

-4- 如果模板参数 T 的模板参数将类型 "reference to cv1 S" 命名为类型 A 的引用,则尝试创建类型 "reference to cv2 T" "lvalue-reference to cv T" 创建类型 "reference to cv12 S",其中 cv12 是 cv 限定符 cv1 和 cv2 的并集。忽略冗余 cv 限定符“对 A 的左值引用”,而尝试创建类型“对 cv T 的右值引用”会创建类型 T。

您引用的内容似乎已过时。

【讨论】:

  • 注意:在 C++11 的最终版本中,这最终出现在 8.3.2/5 中(即 dcl.ref 部分的一部分,不再属于 temp.arg.type)。除名称外,措辞没有变化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多