Consider the following code snippet:
template <typename>
struct X { };
extern template struct X<int>;
int main()
{
X<int>{};
}
It compiles and links: live example on godbolt.org. I would expect it not to link due to the extern template
declaration.
My understanding is that extern template
means: "please don't instantiate this particular template specialization in this TU, it will be provided by some other TU and you can link against it".
The examples/descriptions. I've seen on isocpp and cppreference seem to validate my mental model. E.g.
From https://en.cppreference.com/w/cpp/language/class_template:
An explicit instantiation declaration (an extern template) skips implicit instantiation step: the code that would otherwise cause an implicit instantiation instead uses the explicit instantiation definition provided elsewhere (resulting in link errors if no such instantiation exists). This can be used to reduce compilation times by explicitly declaring a template instantiation in all but one of the source files using it, and explicitly defining it in the remaining file.
Why does my code snippet link? What is actually happening here?
EDIT - found this in the latest Standard draft:
[temp.explicit]
If an entity is the subject of both an explicit instantiation declaration and an explicit instantiation definition in the same translation unit, the definition shall follow the declaration. An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed, no diagnostic required.
Does this mean that the code snippet I posted is ill-formed, NDR?
You can use explicit instantiation to create an instantiation of a templated class or function without actually using it in your code. Because it's useful when you're creating library ( . lib ) files that use templates for distribution, uninstantiated template definitions aren't put into object ( . obj ) files.
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
Implicit instantiation means that the compiler automatically generates the concrete function or class for the provided template arguments. In general, the compiler also deduces the template arguments from the function's arguments. In C++17, the compiler can also deduce the template arguments for class templates.
In C++11 (Introduction to C++11), extern template is added to optimize the compile time and object size. Earlier C++ compiler (C++ 03) instantiate a template function or class whenever it encounters a fully defined template function or class.
Why does my code snippet link? What is actually happening here?
Well, there's nothing to link. For one has to consider the effects of the explicit instantiation. From n3337:
[temp.explicit] (emphasis mine)
10 Except for inline functions and class template specializations, explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer. [ Note: The intent is that an inline function that is the subject of an explicit instantiation declaration will still be implicitly instantiated when odr-used ([basic.def.odr]) so that the body can be considered for inlining, but that no out-of-line copy of the inline function would be generated in the translation unit. — end note ]
So the implicit instantiation of the class template specialization X<int>
, is not suppressed. It's also an aggregate, so its initialization occurs inline, and we get nothing to link against. However, if it had any members, those would be suppressed under paragraph 8:
An explicit instantiation that names a class template specialization is also an explicit instantiation of the same kind (declaration or definition) of each of its members (not including members inherited from base classes) that has not been previously explicitly specialized in the translation unit containing the explicit instantiation, except as described below.
So if you had instead of an aggregate something akin to this:
template <typename>
struct X {
X();
};
template <typename T>
X<T>::X() {}
extern template struct X<int>;
int main()
{
X<int>{};
}
That would fail as you expect, since it ODR uses a constructor whose definition is never instantiated. The declaration is instantiated, because the enclosing specialization is instantiated, as mentioned above. But we never get any definition, under the suppressing effect of the explicit instantiation declaration.
Does this mean that the code snippet I posted is ill-formed, NDR?
Yes, by the exact sentence from [temp.explicit]/13 that you quoted. "An entity" means just that. It does not matter if an explicit instantiation declaration otherwise have no normative effect.
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