static_assert fails check on templated object pointer - c++

template <size_t N>
class Foo
{
static_assert(N > 0, "WRONG");
//void Something() = 0; //my original implementation
};
int main() {
Foo<0> *p2 = nullptr; //no error
Foo<0> p; //gives an error
return 0;
}
I've tested both the lines separately. static_assert is not called when p2 is initialized but it is called and does indeed fail on p. Is this intended? (I've tried it on gcc, clang and VC)
What are the workarounds? Since I'm using abstract templated classes, it would be a nightmare if the assertion is only performed when a non-pointer object is instantiated. I can use a factory but that isn't exactly a proper solution.

You assuredly saw this quote from §14.7.1/1:
Unless a class template specialization has been explicitly
instantiated (14.7.2) or explicitly specialized (14.7.3), the class
template specialization is implicitly instantiated when the
specialization is referenced in a context that requires a
completely-defined object type or when the completeness of the class
type affects the semantics of the program.
Pointer types do not require their pointee to be a complete type (e.g. void* is an example of this). Thus the first line will not instantiate the specialization, but the second one needs to, hence the assertion fires only on that one.
This is also addressed by an example three paragraphs further down:
[ Example:
template<class T> struct Z {
void f();
void g();
};
void h() {
Z<int> a; // instantiation of class Z<int> required
Z<double>* q; // instantiation of class Z<double> not required
//[…]
}
Nothing in this example requires class Z<double> […] to be implicitly instantiated. — end example ]

Related

Forward declare C++ [duplicate]

The following compiles on GCC 4.8.1 (with --std=c++11):
struct non_default_constructible { non_default_constructible() = delete; };
template<class T>
struct dummy {
T new_t() { return T(); }
};
int main(int argc, char** argv) {
dummy<non_default_constructible> d;
return 0;
}
The tricky part is that dummy<non_default_constructible>::new_t() is obviously ill-formed, but that does not prevent the compiler from instantiating dummy<non_default_constructible>.
Is this the behaviour specified by the standard? And what would be the relevant sections/keywords?
The member functions of a class template are instantiated only when required by a context, which means you will not see any error until you try to use new_t(). The related section from the C++ standard is:
§ 14.7.1 Implicit instantiation [temp.inst]
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.
[ Example:
template<class T> struct Z {
void f();
void g();
};
void h() {
Z<int> a; // instantiation of class Z<int> required
Z<char>* p; // instantiation of class Z<char> not required
Z<double>* q; // instantiation of class Z<double> not required
a.f(); // instantiation of Z<int>::f() required
p->g(); // instantiation of class Z<char> required, and
// instantiation of Z<char>::g() required
}
Nothing in this example requires class Z<double>, Z<int>::g(), or Z<char>::f() to be implicitly
instantiated. — end example ]

Why do const variables don't need to be initialized in a template class until the class is actually used?

Why doesn't the compiler throw an error if I don't initialize const variables in an unused template class? If I remove the template keyword the compiler complains as expected.
This code works (unexpected):
#include <iostream>
template<class T>
class Matrix {
private:
const uint8_t numRows, numColumns;
T content[];
public:
Matrix(uint8_t numRows, uint8_t numColumns)
// : numRows(numRows), numColumns(numColumns) -- Works fine
{
std::cout << "Matrix" << std::endl;
}
};
int main() {
// If Matrix constructor is not called the compiler doesn't need a initialization list
// Matrix<int> matrix(2, 2);
return 0;
}
But this one does not (expected):
#include <iostream>
template<class T>
class Matrix {
private:
const uint8_t numRows, numColumns;
T content[];
public:
Matrix(uint8_t numRows, uint8_t numColumns)
// : numRows(numRows), numColumns(numColumns) -- Does NOT work
{
std::cout << "Matrix" << std::endl;
}
};
int main() {
Matrix<int> matrix(2, 2);
return 0;
}
Class template isn't implicitly instantiated until required. You don't use Matrix<int>, then it's not instantiated, and definition is not required (to exist or to be well-formed).
When code refers to a template in context that requires a completely defined type, or when the completeness of the type affects the code, and this particular type has not been explicitly instantiated, implicit instantiation occurs. For example, when an object of this type is constructed, but not when a pointer to this type is constructed.
From the standard, [temp.inst]/2:
Unless a class template specialization is a declared specialization, the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program.
[temp.inst]/11:
(emphasis mine)
An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class or static data member of a templated class, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required.
So in this case, the ill-formed Matrix<T>::Matrix(uint8_t numRows, uint8_t numColumns) is not allowed to be implicitly instantiated.
The code is ill-formed, no diagnostic required (meaning the code is invalid, but compilers are not required to detect this fact).
This is caused by [temp.res.general]/6.4:
The program is ill-formed, no diagnostic required, if:
— a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, ...

Incomplete type is not allowed in a class, but is allowed in a class template

The following is invalid code:
struct foo {
struct bar;
bar x; // error: field x has incomplete type
struct bar{ int value{42}; };
};
int main() { return foo{}.x.value; }
This is quite clear, as foo::bar is considered incomplete at the point where foo::x is defined.
However, there seems to be a "workaround" which makes the same class definition valid:
template <typename = void>
struct foo_impl {
struct bar;
bar x; // no problems here
struct bar{ int value{42}; };
};
using foo = foo_impl<>;
int main() { return foo{}.x.value; }
This works with all major compilers.
I have three questions about this:
Is this indeed valid C++ code, or just a quirk of the compilers?
If it is valid code, is there a paragraph in the C++ standard that deals with this exception?
If it is valid code, why is the first version (without template) considered invalid? If the compiler can figure out the second option, I don't see a reason why it wouldn't be able to figure out the first one.
If I add an explicit specialization for void:
template <typename = void>
struct foo_impl {};
template<>
struct foo_impl<void> {
struct bar;
bar x; // error: field has incomplete type
struct bar{ int value{42}; };
};
using foo = foo_impl<>;
int main() { return foo{}.x.value; }
It once again fails to compile.
The real answer might be ¯\_(ツ)_/¯, but it's probably currently okay because templates are magical, but it may be more explicitly not okay pending some other core issue resolutions.
First, the main problem of course is [class.mem]/14:
Non-static data members shall not have incomplete types.
This is why your non-template example is ill-formed. However, according to [temp.point]/4:
For a class template specialization, a class member template specialization, or a specialization for a class member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization, if the context from which the specialization is referenced depends on a template parameter, and if the specialization is not instantiated previous to the instantiation of the enclosing template, the point of instantiation is immediately before the point of instantiation of the enclosing template. Otherwise, the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
Which suggests that foo_impl<void>::bar is instantiated before foo_impl<void>, and hence it's complete at the point where the non-static data member of type bar is instantiated. So maybe it's okay.
However, core language issues 1626 and 2335 deal with not-exactly-the-same-but-still-quite-similar issues regarding completeness and templates, and both point to desiring to make the template case more consistent with the non-template case.
What does all of this mean when viewed as a whole? I'm not sure.
I think this example is explicitly allowed by
17.6.1.2 Member classes of class templates [temp.mem.class]
1
A member class of a class template may be defined outside the class template definition in which it is declared.
[Note: The member class must be defined before its first use that requires an instantiation (17.8.1) For example,
template<class T> struct A {
class B;
};
A<int>::B* b1; // OK: requires A to be defined but not A::B
template<class T> class A<T>::B { };
A<int>::B b2; // OK: requires A::B to be defined
—end note ]
This should work fine too:
template <typename = void>
struct foo_impl {
struct bar;
bar x; // no problems here
};
template<typename T>
struct foo_impl<T>::bar{ int value{42}; };
using foo = foo_impl<>;
int main()
{
return foo{}.x.value;
}
More details about the accepted answer
I am not sure that the accepted answer is the correct explanation, but it is the most plausible one for now. Extrapolating from that answer, here are the aswers to my original questions:
Is this indeed valid C++ code, or just a quirk of the compilers? [ It is valid code. ]
If it is valid code, is there a paragraph in the C++ standard that deals with this exception? [ [temp.point]/4 ]
If it is valid code, why is the first version (without template) considered invalid? If the compiler can figure out the second option, I don't see a reason why it wouldn't be able to figure out the first one. [ Because C++ is weird - it handles class templates differently than classes (you could have probably guessed this one). ]
Some more explanations
What seems to be happening
When instantiating foo{} in main the compiler instantiates an (implicit) specialization for foo_impl<void>. This specialization references foo_impl<void>::bar on line 4 (bar x;). The context is within a template definition so it depends on a template parameter, and the specialization foo_impl<void>::bar is obviously not previously instantiated, so all the preconditions for [temp.point]/4 are fulfilled, and the compiler generates the following intermediate (pseudo)code:
template <typename = void>
struct foo_impl {
struct bar;
bar x; // no problems here
struct bar{ int value{42}; };
};
using foo = foo_impl<>;
// implicit specialization of foo_impl<void>::bar, [temp.point]/4
$ struct foo_impl<void>::bar {
$ int value{42};
$ };
// implicit specialization of foo_impl<void>
$ struct foo_impl<void> {
$ struct bar;
$ bar x; // bar is not incomplete here
$ };
int main() { return foo{}.x.value; }
About specialization
As per [temp.spec]/4:
A specialization is a class, function, or class member that is either instantiated or explicitly specialized.
so the call to foo{}.x.value in the original implementation with templates qualifies as a specialization (this was something new to me).
About the version with explicit specialization
The version with explicit specialization does not compile as it seems that:
if the context from which the specialization is referenced depends on a template parameter
no longer holds, so the rule from [temp.point]/4 does not apply.
I'll answer the third part of your question - as IANALL (not a language lawyer).
The code is invalid for the same reason it's invalid to use a function before it has been declared - even though the compiler can figure out what the function's supposed to be by going further down in the same translation unit. And the cases are similar also in the sense that if you happen to have just a declaration with no definition, that's good enough for the compiler, while here you happen to have a template definition before the instantiation.
So the point is: The language standard mandates that the compiler does not look ahead for you when you want to define something (and a class template is not a definition of a class).

Instantiation of template

If I have the following code:
template <typename T = int>
struct mystruct {
using doublestruct = mystruct<double>;
}
mystruct<>::doublestruct obj;
Does this instantiate the mystruct<int> template at all? Or only the mystruct<double> is instantiated?
Yes, it will have to instantiate mystruct<int> in order to access its members and determine the meaning of doublestruct. You could test this with a static_assert:
#include <type_traits>
template <typename T = int>
struct mystruct {
static_assert(!std::is_same<T,int>::value, "");
using doublestruct = mystruct<double>;
};
mystruct<>::doublestruct obj; // assertion fails for T==int
mystruct<char>::doublestruct obj; // OK, not instantiated for int
Yes, it must be instantiated; doublestruct is a member of the instantiation so, if you do not have an instantiation, you do not have a doublestruct.
[C++11: 14.7.1]: Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. [..]
In particular, consider the potential effect of specialisations of mystruct that may not contain a member doublestruct, or may contain one that is not a type.

Why does function calls to templatized base classes not work?

Consider the following example:
template <typename T>
class A {
public:
void f() {
cout << "A::f()\n";
}
};
template<>
class A<int> {
};
template<typename T>
class B: public A<T> {
public:
void g() {
cout << "B::g()\n";
A<T>::f();
}
};
int main() {
B<int> b; // (1)
b.g(); // (2)
return 0;
}
Obviously the call to A::f() inside B::g() will fail for int template type. My question is at what point does the call fail? At (1) or (2)? I thought it should be (1) because at that point the compiler creates a new class with the template type int and compiles it. That compilation should fail in f() correct?
It will fail at (2), and this is guaranteed by the standard. In section 14.7.1/1, it says instantiating a template class does not instantiate it's members definitions. That will only happen once the member is used.
If you remove (2) from the code, it will compile.
14.7.1/1 excerpt:
The implicit instantiation of a class template specialization causes the implicit
instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; and it causes the implicit instantiation of the definitions of member anonymous unions.
Emphasis mine.
Visual Studio's diagnosis is misleading. It will say see reference to class template instantiation 'B<T>' being compiled. What it means is not "I'm failing at the instantiation of B<T>", but "I'm failing at instantiating a member of the class B<T>"
It fails at 2). Member function of templates are instantiated when called.
More precisely: When a class template is instantiated, the declaration of its member functions are instantiated, but not their definition. The definition is instantiated when the function is used.