Basically, an explicit instantiation directive is interpreted according to the following 3 step procedure:
- Perform name lookup on the name of the explicit instantiation to determine a list of "candidate" templates.
- Perform template argument deduction to determine whether any of those templates are "viable".
- If there are multiple "viable" templates (this can only occur in the case of a function template, since other kinds of templates cannot be overloaded), but one of them is more specialized than all the other ones, the explicit instantiation directive explicitly instantiates that template. If not, then the program is ill-formed.
I borrowed the terms "candidate" and "viable" from overload resolution terminology. They are in scare quotes to remind you that I am using them in a way in which the standard doesn't.
We'll now go over the language wording for each of these.
Step 1 is not that relevant to your question, since obviously the f
in // 3
has // 1
and // 2
as candidates. But there are explicit rules about this in the C++23 draft (I don't think they're in C++17 or C++20), [dcl.meaning.general]/3:
Otherwise:
- If the id-expression in the declarator-id of the declarator is a qualified-id Q, let S be its lookup context (6.5.5); the declaration shall inhabit a namespace scope.
- Otherwise, let S be the entity associated with the scope inhabited by the declarator.
- If the declarator declares an explicit instantiation or a partial or explicit specialization, the declarator does not bind a name. If it declares a class member, the terminal name of the declarator-id is not looked
up; otherwise, only those lookup results that are nominable in S are considered when identifying any function template specialization being declared (13.10.3.7).
[...]
In your case, the declarator-id in // 3
is unqualified, so S is the namespace that these declarations inhabit (e.g., if your code represents a complete translation unit, then S is the global namespace) and the "candidates" are the results of unqualified name lookup for f
that are nominable in S. According to [basic.scope.scope]/6, only those declarations that have a target scope of S or a namespace that belongs to the inline namespace set of S are nominable [1]. Since // 1
and // 2
have a target scope of S, they are candidates.
Steps 2 and 3 are described by C++17 [temp.deduct.decl] (I refer to C++17 because that was the current standard at the time this question was posted):
In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done
for explicit instantiations (17.7.2), explicit specializations (17.7.3), and certain friend declarations (17.5.4). This is also done to determine whether a deallocation function template specialization matches a placement
operator new
(6.7.4.2, 8.3.4). In all these cases, P
is the type of the function template being considered as a potential match and A
is either the function type from the declaration or the type of the deallocation
function that would match the placement operator new
as described in 8.3.4. The deduction is done as described in 17.8.2.5.
If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (17.5.6.2), deduction fails and, in the declaration cases, the program is
ill-formed.
I'll skip over the details of the type deduction since it's obvious that it succeeds with both // 1
and // 2
, where bool A
is obtained from the explicitly specified template argument true
and in the case of // 1
, X
is deduced as int
.
So finally we have step 3: partial ordering, as described in 17.5.6.2 ([temp.func.order]). I quote the relevant passages:
Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If
so, the more specialized template is the one chosen by the partial ordering process.
To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (17.5.3) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template. [...]
Using the transformed function template’s function type, perform type deduction against the other template as described in 17.8.2.4.
17.8.2.4 [temp.deduct.partial] explains how the result of the deduction is used to determine which template is more specialized (p8):
[...] If deduction succeeds for a given type, the type from the argument template is considered to be at least as specialized as the type from the parameter template.
p12 contains the following important proviso:
In most cases, deduction fails if not all template parameters have values, but for partial ordering purposes a template parameter may remain without a value provided it is not used in the types being used for partial ordering. [Note: A template parameter used in a non-deduced context is considered used. — end note]
[Example:
template <class T> T f(int); // 1
template <class T, class U> T f(U); // 2
void g() {
f<int>(1); // calls #1
}
—end example]
In the example given in the standard, we are in the context of a function call, so the types used for the deduction are the function parameter types for which arguments were provided (p3.1). Since T
(in both templates) is only used in the return type T
, it is not part of any of the types used for deduction. That means that the deduction process succeeds when deducing // 2
from // 1
(because U
can be deduced as int
) and doesn't succeed in the other direction. That implies that // 1
is more specialized.
In the OP's code, the types of the functions themselves are used because this is not a function call context (p3.1). A
cannot be deduced in either direction, but it also doesn't participate in the function types, so the fact that it can't be deduced is ignored. Clearly, X
can be deduced in // 1
from the type of // 2
. This tells us that // 2
is at least as specialized as // 1
. If we do it the other way around, the unique type synthesized for X
will not match the int
in // 2
. So // 1
is not at least as specialized as // 2
.
Clang and MSVC are right. // 2
, being the unambiguously more specialized template, is explicitly instantiated. GCC is wrong. Fedor provided (in the comments) a link to the GCC bug report. Comment 3 is very similar to your example.
[1] The nominability requirement stated here—both for qualified and unqualified declarator-ids—basically just says that you have to state the exact scope of the entity you're instantiating or specializing. If you use an unqualified name f
, then it won't explicitly instantiate any template named f
from an enclosing namespace. If you use a qualified name such as N::f
, it won't explicitly instantiate an f
from an enclosing namespace of N
. This is how compilers have worked since C++98 but I don't think the rules were ever explicitly written down before C++23.