C++14中,prvalues的Cv限定符资格

13
看起来C++11和C++14对于prvalues的cv限定符处理方式有所不同。
C++11坚持自C++98以来一直存在的“经典”方法:根据3.10/4,“非类prvalues始终具有cv未限定类型”。
C++14在3.10/4中包含类似的措辞,但是它被呈现为一个注释:“[注:类和数组prvalues可以具有cv限定类型;其他prvalues始终具有cv未限定类型。参见第5条。-end note]”
而在第5条中则说:
6 如果prvalue最初具有类型“cv T”,其中T是cv未限定的非类、非数组类型,则在进一步分析之前将表达式的类型调整为T。1
这个5/6的条目是C++14中新增的。它现在使用与引用类型结果一直使用的相同方法来处理prvalues的cv限定符(请参见5/5)。
这个改变的原因是什么?C++11及更早版本否认非类prvalues拥有任何cv限定符的权利。C++14表示,非类、非数组prvalues可以具有cv限定符,但是这些cv限定符在进一步分析之前会被丢弃。
我猜测可能有一些新的(对于C++14而言)语言特性,在正确的情况下可以“看到”prvalues的cv限定符(在上述调整发生之前)。它们存在吗?如果是这样,这些特性是什么?
问题源于以下背景:想象一个编译器,在内部将类X的隐藏参数this实现为类型为X *const的变量。由于编译器需要将this公开为prvalue,在C++11(或之前)中标量prvalues从不具有cv限定符,因此该常量不应导致任何问题。但是C++14呢?如果完全相同的编译器将此作为类型为X *const的prvalue公开,是否可能会导致问题?
1 5/6和C++14中3.10/4的注释似乎存在矛盾,但注释本身并不具有规范性。而且我正在使用文本的草案版本。
2 我最初的猜测是decltype。当我尝试时,我甚至认为找到了答案。
std::cout << std::is_same<decltype((const int) 0), const int>::value << std::endl;

在 GCC 中,输出 1。然而,考虑到 Clang 和 VC++ 输出 0(且 decltype 规范似乎不支持此行为),我倾向于认为这只是 GCC(从 6.1 开始)中的一个错误。


5
这是有关概念行为的问题。我提出这个问题的原因是,我找不到任何可以利用C++11和C++14之间差异的代码示例。如果该差异无法通过代码演示,那么我想知道是什么促使了标准文本的更改。如果该差异可以通过代码演示,那么看到这段代码会很好。 - AnT stands with Russia
4
@Thomas 这绝对是需要语言律师介入的领域。 - user2100815
5
@Neil Butterworth:好的,那么新措辞在哪些方面更“准确或细致入微”?这基本上就是我的问题所涉及的。 - AnT stands with Russia
4
这是核心问题1261,公共问题列表中没有任何讨论,但我认为这只是措辞上的澄清。例如,考虑const int f(),则f()是prvalue,其类型根据[expr.call],“静态选择函数的返回类型”,将是const int,因此需要某种措辞来消除const - T.C.
4
是的,GCC曾经在处理像 S*&& p = this; 这样的代码时存在问题。现在它们已经解决了这个问题。 - T.C.
显示剩余8条评论
1个回答

3
根据 GitHub 上的提交记录,这是为了解决 CWG1261:显式处理带有非类 prvalue 的 cv 限定符
根据问题的评论,似乎 this(正式为 prvalue)的类型类别存在出乎意料的变化余地,而gcc 以前和目前的 MSVC 则使用了一个const lvalue
措辞严谨地弥补了漏洞,明确指出,即使this是某种编译器内部魔术的类型为X* const的 prvalue,在进行任何进一步分析之前,它也会被调整为X*
同样,你给出的例子看起来像是 gcc 的一个 bug。可能是因为decltype在应用 C 风格转换之前没有查看值类型。
现在它成为 [basic.lval]/4 中的一个注释,原因是它现在是 [expr]/6 中新文本的结果,而不是在 [basic.lval]/4 中规定的规则。
充分感谢 T.C. 在问题的评论中基本上已经回答了这个问题,包括对 gcc bug 修复的引用,以及先前未明确规定的 带有 cv 限定符的非类 non-array prvalue 的各种其他示例。

Jason Merrill 写道:「当 'this' 出现在一个表达式中时,它应该是一个 rvalue 而不是 const lvalue;在 C++11 中,这个区别很重要。」但是,在 C++ 的历史上,rvalue 从来都不像 const lvalue。 - curiousguy
在C++11之前,您无法使用decltype和右值引用来区分它们(但也许const_cast可以做到?)。 - Davis Herring
@DavisHerring 你是指 C * const &that = this; const_cast<C*&>(that) = new C; 吗? - curiousguy
1
@curiousguy:只是 const_cast<C*&>(this) - 给它赋值可能是未定义的行为,但如果它是一个右值,那么转换就不应该编译(如果我正确记得我的C++03)。 - Davis Herring
@DavisHerring 或者简单地说 &this - curiousguy
@curiousguy:我猜它理论上可能是一个临时的,但这是有道理的。 - Davis Herring

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接