Consider this code, with an obvious compile error: (1)
struct A;
struct B {
B() { new A(); } // error: allocation of incomplete type 'A'
};
Using a unique_ptr
will not help either: (2)
struct A;
struct B {
B() { std::make_unique<A>(); } // error: due to ~unique_ptr()
};
Then (to my great surprise) I found out, that this will compile: (3)
struct A;
struct B {
B() { std::make_unique<A>(); }
};
struct A {}; // OK, when a definition is added **below**
Then I checked, whether this helps with new
as well - nope: (4)
struct A;
struct B {
B() { new A(); } // error: allocation of incomplete type 'A'
};
struct A {};
I figured it has something to do with template
s and in fact: wrapping new
inside a template
does compile: (5)
template <typename T>
T* my_new() { return new T(); } // OK, when wrapped in template
struct A;
struct B {
B() { my_new<A>(); }
};
struct A {};
And just for the sake of completeness, removing the definition of A
raises an error again: (6)
template <typename T>
T* my_new() { return new T(); } // error: allocation of incomplete type 'A'
struct A;
struct B {
B() { my_new<A>(); }
};
// do note: definition of A removed
What's going on here? As far as I understood, the compiler must know the size/definition of A
to allocate it, thus merely declaring it, is not sufficient. In addition I believed, that the definition must precede the allocation.
This seems to be correct, when using new
directly (1,4). But when new
is wrapped, it is apparent that I am mistaken (2,3,5,6).
Possible explanations I found so far are:
template
instantiation occurs. I think this is correct, but in my case the direct use of new A()
and the call to my_new<A>()
occur virtually on the same position. So this cannot be the reason. Right?template
Arguments might be undefined behavior. Is this really true? Even when enabling all warnings, the compiler will not complain. Also comparing 5 and 6 seems to suggest, that the compiler is smart enough to figure out, that the definition follows below (thus virtually making the type complete).Why 4 is considered to be incorrect, whilst 5 compiles (or is 5 just spuriously compiling undefined behavior [but then 3 must be flawed as well, right?])?
btw: tested with clang++-3.5.0 and g++-4.9.2
§14.6.4.1 [temp.point]/p1,8, emphasis mine:
1 For a function template specialization, a member function template specialization, or a specialization for a member function or static data member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization and the context from which it is referenced depends on a template parameter, the point of instantiation of the specialization is the point of instantiation of the enclosing specialization. Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization.
8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. A specialization for a class template has at most one point of instantiation within a translation unit. A specialization for any template may have points of instantiation in multiple translation units. If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.
There are two points of instantiation of my_new<A>
, one at the end of the definition of B
, and one at the end of the translation unit. Since those two points will result in different meanings (for snippets 3 and 5), the program is ill-formed NDR (i.e., it has undefined behavior).
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