Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a using declaration of a template specialization always cause its implicit instantiation?

I understand that it is illegal to do the following:


template<typename T>
struct will_get_explicitly_specialized {
    using type = void;
};

template<typename T>
struct ordinary_template {
    using type = typename will_get_explicitly_specialized<T>::type;
};

ordinary_template<int> x;

template<>
struct will_get_explicitly_specialized<int> {
     using type = int;
};

This will (hopefully, as I believe it is not required) generate an error like the following:

<source>:16:8: error: specialization of 'will_get_explicitly_specialized<int>' after instantiation
   16 | struct will_get_explicitly_specialized<int> {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:16:8: error: redefinition of 'struct will_get_explicitly_specialized<int>'
<source>:4:8: note: previous definition of 'struct will_get_explicitly_specialized<int>'
    4 | struct will_get_explicitly_specialized {
      |        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 1

This aligns with my understanding that all explicit specializations of will_get_explicitly_specialized need to come before any implicit instantiations of will_get_explicitly_specialized, and the declaration of ordinary_template<int> x necessarily causes instantiation of ordinary_template<int>, and that appears in turn to require implicit instantiation of will_get_explicitly_specialized<int> due to the using type = statement.

Interestingly though, a top-level using statement at that position in the code does not raise a diagnostic (in the same environment):

template<typename T>
struct will_get_explicitly_specialized {
    using type = void;
};

template<typename T>
struct ordinary_template {
    using type = typename will_get_explicitly_specialized<T>::type;
};

using ot_int = ordinary_template<int>;

template<>
struct will_get_explicitly_specialized<int> {
     using type = int;
};

ot_int x;

I would like to know if this is still illegal (but perhaps no diagnostic is issued and one is not required), or whether this sort of using statement is somehow different from the one in ordinary_template such that it does not count as causing an implicit instantiation of will_get_explicitly_specialized.

In either event, what is the language that makes clear whether/when a specialization of a template in a using statement results in implicit instantiation?

It feels intuitively like it should be ok to say using ot_int = ordinary_template<int> before all the specializations are in, because until you actually use ot_int somehow, nothing has really happened such that ordinary_template<int> needs to exist. But of course just because it feels that way, it doesn't make it true!

like image 292
acm Avatar asked Oct 24 '25 15:10

acm


1 Answers

This will (hopefully, as I believe it is not required) generate an error like the following:

Technically the first program is IFNDR (ill-formed, no diagnostic required) according to [temp.expl.spec]/7 because there is no declaration of the explicit specialization of will_get_explicitly_specialized<int> reachable from ordinary_template<int> x; which however would cause implicit instantiation of that same specialization.

So a compiler is technically not required to diagnose the issue. There is no requirement imposed on the compiler for this program.


Simply naming a specialization with a template-id doesn't cause it to be implicitly instantiated. The relevant section of the standard in [temp.inst]/2 says:

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.

In a definition of a variable such as

ordinary_template<int> x;

or

ot_int x;

the type of the variable must always be complete. This implies in both cases that ordinary_template<int> must be implicitly instantiated at this point (if it hasn't already been instantiated beforehand).

However, simply declaring a type alias has no requirement on the type being complete and neither does the declaration have any different semantic if the type is incomplete.

So, simply aliasing a class template specialization will never cause it to be implicitly instantiated. Your second program therefore doesn't violate the rule mentioned above and is well-formed with defined behavior (lack of main aside). ot_int::type will be int as required by the explicit specialization.

like image 115
user17732522 Avatar answered Oct 26 '25 05:10

user17732522