The following code triggers C4345 on the marked line:
#include <array>
#include <iostream>
int main(){
static unsigned const buf_size = 5;
typedef std::array<char, buf_size> buf_type;
char buf[] = { 5, 5, 5, 5, 5 };
void* p = &buf[0];
buf_type* pbuf = new (p) buf_type(); // <=== #10
for(unsigned i=0; i < buf_size; ++i)
std::cout << (char)((*pbuf)[i] + 0x30) << ' ';
}
main.cpp(10): warning C4345: behavior change: an object of POD type constructed with an initializer of the form () will be default-initialized
So, according to their warning, line 10 should have the same behaviour as if it was written as
buf_type* pbuf = new (p) buf_type; // note the missing '()'
However, the output differes. Namely, the first version will print five 0
s, while the second version will print five 5
s. As such, the first version is indeed value-initialized (and the underlying buffer zero-initialized), even though MSVC says it won't.
Can this be considered a bug in MSVC? Or did I misinterpret the warning / is my test code faulty?
TL;DR version: MSVC's behavior is actually correct, although the warning is incorrect (it should say value-initialized).
For new (p) buf_type;
, MSVC is correct to perform default initialization, because the standard (5.3.4 [expr.new]
) demands:
A new-expression that creates an object of type
T
initializes that object as follows:
- If the new-initializer is omitted, the object is default-initialized (8.5); if no initialization is performed, the object has indeterminate value.
- Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-initialization.
std::array
is a class type. For class types (8.5 [dcl.init]
):
To default-initialize an object of type
T
means:
- if
T
is a (possibly cv-qualified) class type, the default constructor forT
is called (and the initialization is ill-formed ifT
has no accessible default constructor);
default-initialization leaves the memory unchanged only for primitive types (and raw arrays thereof)
On the other hand, std::array
has a defaulted default constructor, so the members themselves ought to be default-initialized. And in fact you observed that.
Then according to the same section, the new (p) buf_type();
version causes direct-initialization.
std::array
is an aggregate, so I think this rule (8.5.1 [dcl.init.aggr]
) then applies:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (8.5.4).
And that means value-initialization for all elements.
Nope, here's the rule (8.5 [dcl.init]
):
An object whose initializer is an empty set of parentheses, i.e.,
()
, shall be value-initialized.
Value initialization of an aggregate means value initialization of all elements, since the elements are primitive, that means zero fill.
So MSVC's behavior is actually correct, although the warning is incorrect (it should say value-initialized).
It's already been reported, see
Here is how I read the MS article http://msdn.microsoft.com/en-us/library/wewb47ee%28v=vs.80%29.aspx
Under the old version of VS (your original syntax) - in this article, VS 2003 - a POD initialize would default initialize with 0s. Your code would support this.
Under the new version of VS - in this article, that would be VS 2005 - it was up to the programmer to initializes the POD explicitly as no default initialization was performed. In your case, as shown in your code, it correctly displays 5s.
Thus, as I read it, the old syntax initialized the POD to 0s even if your code had already explicitly initialized it. I do add a disclaimer that it is very late here and I am tired, so this may all be babbling.
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