考虑以下这段代码:
struct S{
int i;
S(int);
S(const volatile S&);
};
struct S_bad{
int i;
};
volatile S as{0};
volatile S_bad as_bad{0};
volatile int ai{0};
void test(){
ai; //(1)=> a load is always performed
as; //(2)=> Should call the volatile copy constructor
as_bad; //(3)=> Should be ill-formed
}
表达式ai;
,as;
和as_bad
是废弃的值表达式,根据C ++草案标准N4659/[expr].12,我预计应用lvalue-to-rvalue在这三种情况下。对于情况(2),这应该导致调用volatile复制构造函数(S(const volatile S&)
)[expr]/12
[...]如果表达式是此可选转换后的prvalue,则应用临时实现转换([conv.rval])。 [注意:如果表达式是类类型的左值,则它必须具有易失性复制构造函数来初始化临时值,该临时值是左值到右值转换的结果对象。 - 结束说明]
因此案例(3)应该是格式错误的。
尽管如此,编译器的行为似乎很混乱:
GCC
ai;
=>加载ai
; as;
=>没有代码生成,没有警告; as_bad;
=>加载as_bad.i
。Clang不会为case(2)生成负载并生成警告:表达式结果未使用;分配给变量以强制挥发性负载[-Wunused-volatile-lvalue]
ai;
=>加载ai
; as;
=>没有代码生成;警告表达结果未使用;分配给变量以强制挥发性负载[-Wunused-volatile-lvalue] as_bad;
=>与as;
相同。MSVC在两种情况下都会执行加载。
ai;
=>加载ai
; as;
=>加载as.i
(不调用volatile复制构造函数)as_bad;
=>加载as_bad.i
。根据标准我的预期摘要:
ai;
=>加载ai
; as;
=>以S(const volatile S&)
为参数调用as
; as_bad;
=>生成编译错误我对标准的解释是对的吗?哪个编译器是正确的?
答案 0 :(得分:2)
b ? (x,y) : z
那样愚蠢的事情,如果y
没有计算),并添加关于易失性复制构造函数的说明。所以我的结论是(从C ++ 11开始)你是对的并且所有编译器都是错误的。特别是,除非您的复制构造函数读取它,否则不应发生S::i
加载。实现定义的" access"当然,与什么是格式良好的问题无关;它只会影响是否实际生成ai
的加载指令。问题是S_bad
是一个聚合,但这是无关紧要的,因为它没有被列表初始化。