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