volatile class 类型的丢弃值表达式与 volatile 内置类型的行为不同
Discarded value expressions of volatile class type behave differently than those of volatile built-in types
考虑以下这段代码:
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 I expected that an lvalue-to-rvalue would have applied in these three cases. For case (2) this should cause a call to the volatile copy constructor (S(const volatile S&)
) [expr]/12
[...]If the expression is a prvalue after this optional conversion, the temporary materialization conversion ([conv.rval]) is applied. [ Note: If the expression is an lvalue of class type, it must have a volatile copy constructor to initialize the temporary that is the result object of the lvalue-to-rvalue conversion. — end note ]
所以情况 (3) 应该是病式的。
然而,编译器的行为似乎很混乱:
海湾合作委员会:
ai;
=> 加载 ai
的值;
as;
=> 没有生成代码,没有警告;
as_bad;
=> 加载 as_bad.i
.
Clang 不会为情况 (2) 生成负载并生成警告:expression result unused;分配到一个变量中以强制进行易失性加载 [-Wunused-volatile-lvalue]
ai;
=> 加载 ai
的值;
as;
=> 没有生成代码;警告 表达式结果未使用;赋值到变量中以强制进行易失性加载 [-Wunused-volatile-lvalue]
as_bad;
=> 与 as;
. 相同
MSVC 在这两种情况下执行加载。
ai;
=> 加载 ai
的值;
as;
=> 加载 as.i
(不调用可变复制构造函数)
as_bad;
=> 加载 as_bad.i
.
根据标准总结一下我的预期:
ai;
=> 加载 ai
的值;
as;
=> 以 as
作为参数调用 S(const volatile S&)
;
as_bad;
=> 生成编译错误
我对标准的解释正确吗?哪个编译器是正确的?
- C++03 表示表达式语句的结果不会发生 lvalue-to-rvalue 转换,也没有明确说明转换发生时会发生复制。
- C++11 表示,如您所说,转换确实发生在易失性对象上,并且转换涉及复制以创建临时对象。
- C++14 只是清理了措辞(以避免像
b ? (x,y) : z
这样愚蠢的事情,如果 y
算的话)并添加了关于 volatile 复制构造函数的注释。
- C++17 应用临时物化转换来保留以前的含义。
所以我的结论是(从 C++11 开始)你是对的,所有的编译器都是错的。特别是, S::i
加载不应该发生,除非你的复制构造函数读取它。当然,"access" 的 implementation-defined 性质与什么是 well-formed 的问题无关;它只影响 ai
的加载指令是否实际生成。存在 S_bad
作为聚合的问题,但这无关紧要,因为它不是 list-initialized.
考虑以下这段代码:
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 I expected that an lvalue-to-rvalue would have applied in these three cases. For case (2) this should cause a call to the volatile copy constructor (S(const volatile S&)
) [expr]/12
[...]If the expression is a prvalue after this optional conversion, the temporary materialization conversion ([conv.rval]) is applied. [ Note: If the expression is an lvalue of class type, it must have a volatile copy constructor to initialize the temporary that is the result object of the lvalue-to-rvalue conversion. — end note ]
所以情况 (3) 应该是病式的。
然而,编译器的行为似乎很混乱:
海湾合作委员会:
ai;
=> 加载ai
的值;as;
=> 没有生成代码,没有警告;as_bad;
=> 加载as_bad.i
.
Clang 不会为情况 (2) 生成负载并生成警告:expression result unused;分配到一个变量中以强制进行易失性加载 [-Wunused-volatile-lvalue]
ai;
=> 加载ai
的值;as;
=> 没有生成代码;警告 表达式结果未使用;赋值到变量中以强制进行易失性加载 [-Wunused-volatile-lvalue]as_bad;
=> 与as;
. 相同
MSVC 在这两种情况下执行加载。
ai;
=> 加载ai
的值;as;
=> 加载as.i
(不调用可变复制构造函数)as_bad;
=> 加载as_bad.i
.
根据标准总结一下我的预期:
ai;
=> 加载ai
的值;as;
=> 以as
作为参数调用S(const volatile S&)
;as_bad;
=> 生成编译错误
我对标准的解释正确吗?哪个编译器是正确的?
- C++03 表示表达式语句的结果不会发生 lvalue-to-rvalue 转换,也没有明确说明转换发生时会发生复制。
- C++11 表示,如您所说,转换确实发生在易失性对象上,并且转换涉及复制以创建临时对象。
- C++14 只是清理了措辞(以避免像
b ? (x,y) : z
这样愚蠢的事情,如果y
算的话)并添加了关于 volatile 复制构造函数的注释。 - C++17 应用临时物化转换来保留以前的含义。
所以我的结论是(从 C++11 开始)你是对的,所有的编译器都是错的。特别是, S::i
加载不应该发生,除非你的复制构造函数读取它。当然,"access" 的 implementation-defined 性质与什么是 well-formed 的问题无关;它只影响 ai
的加载指令是否实际生成。存在 S_bad
作为聚合的问题,但这无关紧要,因为它不是 list-initialized.