Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using `extern template` to prevent implicit instantiation of a template class

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?

like image 289
Vittorio Romeo Avatar asked Jun 04 '19 17:06

Vittorio Romeo


People also ask

When to use explicit template instantiation?

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.

How will you restrict the template for a specific datatype?

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.

What is implicit instantiation?

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.

What is extern template?

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.


2 Answers

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.

like image 186
StoryTeller - Unslander Monica Avatar answered Oct 20 '22 00:10

StoryTeller - Unslander Monica


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.

like image 35
T.C. Avatar answered Oct 20 '22 00:10

T.C.