Consider this following piece of code:
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
}
The expression ai;
, as;
and as_bad
are discarded value expressions and according to the C++ draft standard 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 ]
So the case (3) should be ill-formed.
Nevertheless, the behavior of compilers seems chaotic:
GCC:
ai;
=> loads the value of ai
;as;
=> no code generated, no warning;as_bad;
=> loads as_bad.i
.Clang does not produce a load for case (2) and generates the warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
ai;
=> loads the value of ai
;as;
=> no code generated; warning expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
as_bad;
=> same as as;
.MSVC performs the load in both cases.
ai;
=> loads the value of ai
;as;
=> loads as.i
(without calling to the volatile copy constructor)as_bad;
=> loads as_bad.i
.Summary of what I expected according to the standard:
ai;
=> loads the value of ai
;as;
=> call S(const volatile S&)
with as
as argument;as_bad;
=> generate a compilation errorIs my interpretation of the standard right? Which compiler is right if any?
b ? (x,y) : z
not counting if y
does) and adds the note about the volatile copy constructor.So my conclusion is that (as of C++11) you are correct and all the compilers are wrong. In particular, the S::i
load shouldn't happen unless your copy constructor reads it. The implementation-defined nature of "access" is irrelevant to the question of what is well-formed, of course; it only affects whether the load instruction for ai
is actually generated. There is the issue of S_bad
being an aggregate, but that is irrelevant since it's not being list-initialized.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With