【问题标题】:Rvalue references in a conditional expression条件表达式中的右值引用
【发布时间】:2026-01-17 06:20:02
【问题描述】:
typedef decltype(true ? (long&&)0 : (long&&)0) T;

应该是什么?

根据 gcc (4.7),它是 long。根据 clang(主干),它是 long&&。这种差异导致 clang 无法编译使用 gcc 4.7 的 libstdc++ 的代码。谁是对的?

UPDATE:正如 ildjarn 指出的那样,Clang 是正确的,正如 Richard Smith 指出的那样,错误 libstdc++ 是由于错误在标准。这是relevant GCC bugrelevant Defect Report

【问题讨论】:

  • 这是不是一些涉及&&的模板代码的简化形式?
  • @NicolBolas:是的。这相关吗?
  • 我想知道的主要问题是它是否只发生在基本类型上,还是也发生在用户定义的类型上?
  • @NicolBolas:用户定义的类型也会发生这种情况。
  • libstdc++ 包含此错误,原因是 C++ 标准中对 std::common_type 的规范不正确。在 Clang 的“C++ 状态”页面上有一个 libstdc++4.7 的补丁来修复这个错误:clang.llvm.org/cxx_status.html

标签: c++ gcc c++11 clang decltype


【解决方案1】:

Clang 是对的。 N3337 §7.1.6.2/4:

decltype(e)所表示的类型定义如下:

  • 如果e 是无括号的id-expression 或无括号的类成员访问,decltype(e)e 命名的实体的类型。如果没有这样的实体,或者如果e 命名了一组重载函数,则程序是非良构的;
  • 否则,如果e 是一个xvalue,则decltype(e)T&&,其中Te 的类型;
  • 否则,如果e是左值,decltype(e)就是T&,其中Te的类型;
  • 否则,decltype(e)e 的类型。

decltype 说明符的操作数是未计算的操作数。

§5/6:

[ 注意:一个表达式是一个xvalue,如果它是:

  • 调用函数的结果,无论是隐式还是显式,其返回类型是对对象类型的右值引用,
  • 对对象类型的右值引用的强制转换
  • 一个类成员访问表达式,指定一个非引用类型的非静态数据成员,其中对象表达式是一个 xvalue,或
  • .* 成员指针表达式,其中第一个操作数是 xvalue,第二个操作数是指向数据成员的指针。

一般来说,这条规则的效果是命名的右值引用被视为左值,而对对象的未命名的右值引用被视为xvalue;对函数的右值引用被视为左值,无论是否命名。 —尾注 ]

我之前很谨慎,文字 0 可能会以某种方式阻止它在这种情况下成为对象类型,但 §3.9/8 澄清了一些事情:

对象类型是一种(可能是 cv 限定的)类型,它不是函数类型、引用类型,也不是 void 类型。

条件运算符不会影响此处的任何内容 – §5.16/4:

如果第二个和第三个操作数是相同值类别的glvalues并且具有相同类型,则结果是该类型和值类别,并且如果第二个或第三个操作数是位域,则结果是位域,或者如果两者都是位域。

在这种情况下,两者都属于相同的值类别(xvalue),并且xvalues是glvalues。

【讨论】:

  • 实际例子是typedef decltype(true ? declval<long>() : declval<long>()) T;,其中declval<T>返回T&&,我只是简化了。这有什么改变吗?
  • @HighCommander4 :不,这完全强化了我的论点,因为它完全符合第 5/6 节中的第一个项目符号。