Difference Between Instantiation and Specialization in C++ Templates

Template specialization and instantiation

"Instantiating a template specialization" typically refers to the process of implicit instantiation: substituting specific template arguments into a template definition to obtain an instantiated class, function, etc. Instantiating a template means instantiating a specialization of that template. Usually, the less precise phrasing is used when we're talking about some arbitrary instantiation in the context of language lawyering. You will also find the expression "instantiation of a template", which is synonymous with the instantiated specialization.

A issue about the order of explicit instantiation and explicit specialization

It's a bit fragmented, but the relevant rule here is [temp.spec]/5

For a given template and a given set of template-arguments, [...]

  • both an explicit instantiation and a declaration of an explicit specialization shall not appear in a program unless the explicit instantiation follows a declaration of the explicit specialization. [...]

When a template is instantiated?

The general rule for implicit instantiation of class templates is as follows

[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. [...]

That in conjunction with this requirement about functions:

[dcl.fct.def.general] (emphasis mine)

2 In a function-definition, either void declarator ; or declarator ; shall be a well-formed function declaration as described in [dcl.fct]. A function shall be defined only in namespace or class scope. The type of a parameter or the return type for a function definition shall not be a (possibly cv-qualified) class type that is incomplete or abstract within the function body unless the function is deleted ([dcl.fct.def.delete]).

Tells us all we need to know to examine your program. Functions declarations don't require the class type to be complete. So...

Pow<double> instantiated here?

No. This is a function declaration that is not a definition. It does not require a complete class type for a parameter. Pow<double> is not implicitly instantiated.

Pow<int> instantiated here?

Yes. This is a function definition, and so an instantiation is required.

Pow<int> pi; // instantiated here?

Was already instantiated due to the function.

So when exactly the template is instantiated?

Strictly when required in a way that affects the semantics of the program.

So is Pow<int> instantiated when func(Pow<int>) is declared?

When func(Pow<int>) is defined.

If I didn't use Pow<int> in main() then has it been instantiated because of its usage in func as the the type of its parameters?

Yes, because you did so in a function definition.

Implicit instantiation of specialization

This is a similar question here.
And now,I try to answer this question again.

A template is a type of infinite, so we can not instantiate a template, we can only be instantiated template specialization.

  • Implicitly instantiated, the current compilation unit requires the use of the template code, the compiler automatically instantiated template specialization.

  • Explicitly instantiated, we manually enter the code causes the compiler to instantiate a template specialization.

  • explicit specialization, given all the template parameters, and gives a non-generic code. Once the template type match, then instantiate this specialization.
  • Partial specialization, some parameters are given template and gives a non-generic code. Once the template type match, then instantiate this specialization.

Why does Clang prefer the primary template over the specialization from C++17?

This is because C++17 allowed a template type argument to be deduced from the type of a non-type argument. [temp.deduct.type]/13:

When the value of the argument corresponding to a non-type template
parameter P that is declared with a dependent type is deduced from
an expression, the template parameters in the type of P are deduced
from the type of the value.

So when we try to match S<int const, nullptr> against the partial specialization, we deduce the partial specialization's template parameter T from two sources:

  • From the first template argument int const, we deduce T = int const
  • From the second template argument (which has type void (*)(int) because the top-level cv-qualification of function parameters are adjusted away), we deduce T = int.

Since we deduced conflicting results, the deduction fails and the partial specialization is not a match.

Similar examples were brought up on the core reflector back in 2019. There was some agreement that this was a defect in the standard, and that deduction from the type of a non-type template argument should only happen for things that are not otherwise deducible.



Related Topics



Leave a reply



Submit