Hiding of member function templates - Which compiler is correct? - c++

I wrote the following code that hiding of member function templates.
#include <iostream>
struct A
{
template<int> void func()
{
std::cout<<"Function tamplate of A"<<std::endl;
}
};
struct B : A
{
template<char> void func()
{
std::cout<<"Function tamplate of B"<<std::endl;
}
using A::func;
};
int main()
{
B().func<0>();
}
This program working in in Clang compiler. Live demo Clang
But, GCC compiler give an ambiguity error.Live demo GCC
source_file.cpp: In function ‘int main()’:
source_file.cpp:22:17: error: call of overloaded ‘func()’ is ambiguous
B().func<0>();
So, Which compiler is correct?

Regarding the example in the OP: as pointed out to me by W.F., what matters here is that those are member function templates. You added a using declaration, which specifies ([namespace.udecl]/15):
When a using-declaration brings names from a base class into a derived
class scope, member functions and member function templates in the
derived class override and/or hide member functions and member
function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base
class (rather than conflicting).
Note how template parameters aren't accounted. And it is Clang that treated the code correctly by hiding the int version.
On the other hand if one examines the example tobi303 suggested under your post, GCC is kinda in the right. This simply isn't specified to be resolved somehow.
For one, there's [temp.fct.spec]/3:
Trailing template arguments that can be deduced or
obtained from default template-arguments may be omitted from the list
of explicit template-arguments. [...] In contexts where deduction is
done and fails, or in contexts where deduction is not done, if a
template argument list is specified and it, along with any default
template arguments, identifies a single function template
specialization, then the template-id is an lvalue for the function
template specialization.
The text in bold indicates that your program is well-formed only if the template argument we give nominates a single specialization. And ostensibly, it doesn't, since according to [temp.arg.nontype]/1:
A template-argument for a non-type, non-template template-parameter
shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted constant expression of the type of the
template-parameter;
And 0 fits both overloads as a converted constant expression. On account of there not being any ICS ranking for template arguments, this is ambiguous.

Related

Template instantiation of templated class constructor

This fails to compile with clang++, can anybody explain why ? (this compiles fine with g++)
struct X
{
template <typename T> X() {}
};
template X::X<int>();
int main() { return 1; }
instantiate.cc:7:13: error: qualified reference to 'X' is a constructor name rather than a type in this context
template X::X<int>();
^
instantiate.cc:7:14: error: expected unqualified-id
template X::X<int>();
^
2 errors generated.
Constructors don't have names. As much is said in [class.ctor]/1. They are special members that get defined by using the name of the class. But they themselves are nameless. While C++ allows us to reference c'tors in certain contexts by using the class name, those are limited. In general, we cannot name a c'tor.
And that is where the issue lies. To explicitly specify template arguments we must use the name of the templated entity. Constructors don't have names, and so we cannot specify their arguments explicitly.
This is the subject of a note in [temp.arg.explicit] that summarizes the intent of the normative text.
7 [ Note: Because the explicit template argument list follows the
function template name, and because conversion member function
templates and constructor member function templates are called without
using a function name, there is no way to provide an explicit template
argument list for these function templates.  — end note ]
We can still instantiate or specialize constructors, but only if the template arguments don't have to be explicitly specified (if they are deducible, or come from a default template argument). E.g
struct X
{
template <typename T> X(T) {}
};
template X::X(int); // This will work
So Clang is not wrong to reject your code. And it's possible GCC is offering an extension. But ultimately, the standard doesn't offer a way to provide template arguments explicitly to constructor templates.
And upon further digging, there's CWG581, further confirming Clang's behavior is the intended one. It also seems to have made its way into the latest standard revision with some changes to the normative text.
I think Clang is correct. Even Gcc allows it, the templated constructor can't be used at all. The template parameter can't be deduced and we can't specify template argument explicitly for constructor template.
[temp.arg.explicit]/2
Template arguments shall not be specified when referring to a specialization of a constructor template ([class.ctor], [class.qual]).
[temp.arg.explicit]/8
[ Note: Because the explicit template argument list follows the function template name, and because constructor templates ([class.ctor]) are named without using a function name ([class.qual]), there is no way to provide an explicit template argument list for these function templates. — end note ]

Template disambiguator for dependent names

I encountered the following problem by compiling the following example:
template <int N>
class Matrix {
public:
template <int Idx>
int head() {
return Idx;
}
};
template <typename T>
class Test {
static constexpr int RayDim = 3;
public:
int func() const {
Matrix<RayDim> yF;
return yF.head<1>();
// ^ is template keyword required here?
}
};
struct Empty {};
void test() {
Test<Empty> t;
}
Link to Compiler Explorer:
https://godbolt.org/z/js4XaP
The code compiles with GCC 9.2 and MSVC 19.22 but not with clang 9.0.0. Clang states that a template keyword is required. If static constexpr int RayDim = 3; is moved into int func() const clang accepts it.
As stated as a comment in the code block, is the template keyword required for yF.head<1>()?
The template keyword should not be required here, so clang is incorrect to reject the program.
All C++ Standard section and paragraph numbers and quotes below are the same for C++17 draft N4659 and the current linked C++20 draft.
The requirement for template after a . or -> or :: token when naming a member template is in [temp.names]/4. The paragraph first lists cases where the keyword is not allowed, then cases where it is optional and doesn't make a difference, then:
In all other contexts, when naming a template specialization of a member of an unknown specialization ([temp.dep.type]), the member template name shall be prefixed by the keyword template.
A "member of an unknown specialization" is a member of a dependent type which is not "the current instantiation". So the question is whether Matrix<RayDim> is a dependent type. For that, we look at [temp.dep.type]/9:
A type is dependent if it is
a template parameter,
a member of an unknown specialization,
a nested class or enumeration that is a dependent member of the current instantiation,
a cv-qualified type where the cv-unqualified type is dependent,
a compound type constructed from any dependent type,
an array type whose element type is dependent or whose bound (if any) is value-dependent,
a function type whose exception specification is value-dependent,
a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent or is a pack expansion, or
denoted by decltype(expression), where expression is type-dependent.
Matrix<RayDim> is clearly not a template parameter, any kind of member, cv-qualified, an array type, a function type, or specified by decltype. It is a compound type, but it uses only a template-name and an expression, so is not constructed from any other type.
That leaves the simple-template-id case. The template name Matrix is not a template parameter. The template argument RayDim is an expression, so now to see if it is type-dependent or value-dependent.
"Type-dependent" is defined in [temp.dep.expr]. Only paragraph 3 can apply to a lone identifier like RayDim:
An id-expression is type-dependent if it contains
an identifier associated by name lookup with one or more declarations declared with a dependent type,
an identifier associated by name lookup with a non-type template-parameter declared with a type that contains a placeholder type,
an identifier associated by name lookup with a variable declared with a type that contains a placeholder type ([dcl.spec.auto]) where the initializer is type-dependent,
an identifier associated by name lookup with one or more declarations of member functions of the current instantiation declared with a return type that contains a placeholder type,
an identifier associated by name lookup with a structured binding declaration whose brace-or-equal-initializer is type-dependent,
the identifier __func__ ([dcl.fct.def.general]), where any enclosing function is a template, a member of a class template, or a generic lambda,
a template-id that is dependent,
a conversion-function-id that specifies a dependent type, or
a nested-name-specifier or a qualified-id that names a member of an unknown specialization;
or if it names a dependent member of the current instantiation that is a static data member of type "array of unknown bound of T" for some T ([temp.static]).
RayDim certainly does not contain any __func__, template-id, conversion-function-id, nested-name-specifier, or qualified-id. Name lookup finds the class template's static member declaration. That declaration of RayDim is certainly not a template-parameter, a member function, or a structured binding declaration, and its type const int is certainly not a dependent type or array type and does not contain a placeholder type. So RayDim is not type-dependent.
"Value-dependent" is defined in [temp.dep.constexpr]. The only cases that can apply to a lone identifier like RayDim are in paragraph 2:
An id-expression is value-dependent if:
it is type-dependent,
it is the name of a non-type template parameter,
it names a static data member that is a dependent member of the current instantiation and is not initialized in a member-declarator,
it names a static member function that is a dependent member of the current instantiation, or
it is a constant with literal type and is initialized with an expression that is value-dependent.
From above, RayDim is not type-dependent. It is certainly not a template parameter or a member function. It is a static data member and dependent member of the current instantiation, but it is initialized in the member-declarator. That is, the "= 3" appears inside the class definition, not in a separate member definition. It is a constant with literal type, but its initializer 3 is not value-dependent.
So RayDim is not value-dependent or type-dependent. Therefore Matrix<RayDim> is not a dependent type, yF.head is not a member of an unknown instantiation, and the template keyword before head is optional, not required. (It is permitted since it's not in a "type-only context" and head does in fact name a member template.)
Disclaimer
this is not an answer, but rather a long comment
My language-lawyer skills are too low to fully understand the standard but here are a few things I discovered from experimenting with the code. Everything that follows is based on my (far from perfect) understanding of the matter and probably needs some reviews.
Investigation
First off, I went to a fully defined standard version (C++17) so we operate on a well defined implementation.
Looking at this code it seems like MSVC still has some problems (with its lookup, I guess?) when it comes to template instantiation and redefinition. I wouldn't trust MSVC that much in our scenario.
That beeing said, lets think about why we could need the template keyword at
return yF.template head<1>();
Dependent names
Sometimes, inside templates, we have to help the compiler decide whether a name refers to
a value int T::x = 0,
a type struct T::x {};, or
a template template <typename U> T::foo<U>();
If we refer to a value, we do nothing. If we refer to a type, we have to use typename. And if we refer to a template we use template. More on that subject can be found here.
I don't understand the standard specification when it comes to the actual definition of a dependent name but here are some observations.
Observations
Lets take a look at the reference code
template <int N>
struct Matrix
{
template <int Idx>
int head() { return Idx; }
};
template <typename T>
struct Test
{
static constexpr int RayDim = 3;
int func() const
{
Matrix<RayDim> yF;
return yF.head<1>(); // clang complains, gcc and msvc are ok
}
};
struct Empty {};
int test()
{
Test<Empty> t;
return t.func();
}
Usually RayDim should be a dependent name (because it's inside the template Test) which would cause Matrix<RayDim> to be a dependent name aswell. For now, lets assume that Matrix<RayDim> actually is a dependent name. This makes Matrix<RayDim>::head a dependent name aswell. Since Matrix<RayDim>::head is a templated function it is a template in itself and the rules of dependent names from above apply, requiring us to use the template keyword. This is what clang is complaining about.
However, since RayDim is defined inside Test and func is also defined inside the same template and not a templated function in itself, I don't think RayDim actually is a dependent name in the context of func. Furthermore, RayDim doesn't rely on the template arguments of Test. In this case, Matrix<RayDim> and Matrix<RayDim>::head respectively, would become non-dependent names, which allows us to omit the template keyword. This is why gcc (and msvc) compile.
If we were to template RayDim aswell, like here
template <typename>
static constexpr int RayDim = 3;
gcc would treat it as a dependent name aswell (which is correct, since there might be a template specialization later so we don't know at that point). Meanwhile, msvc happily accepts everything we throw at it.
Conclusion
It seems like it's boiling down to whether RayDim is a dependent name in the context of Test<T>::func or not. Clang thinks it is, gcc doesn't. From some more test it looks like msvc sides with clang on this one. But it's also kinda doing it's own thing, so who knows?
I would side with gcc here as I see no possible way of RayDim becoming dependent at the point where func is instantiated.

convert member function to pointer to member function

Clang,GCC,MSVC have different opinion about conversion of member functions.
Who is right ?
https://gcc.godbolt.org/z/QNsgwd
template<typename T>
struct a
{
template <typename... Args>
void va(Args...) {}
template <typename X>
void x(X) {}
void y(int) {}
};
struct b : a<b>
{
void testva()
{
using F = void (a<b>::*)();
F f = (F)&a<b>::va<int>; // gcc: error, msvc: error, clang: ok
}
void testx()
{
using F = void (a<b>::*)();
F f = (F)&a<b>::x<int>;// gcc: error, msvc: ok, clang: ok
}
void testy()
{
using F = void (a<b>::*)();
F f = (F)& a<b>::y; // gcc: ok, msvc: ok, clang: ok
}
};
testx and testy are well-formed, so gcc is wrong about testx. But the Standard is somewhat vague about testva.
Starting with the easiest, in testy the expression &a<b>::y names a non-template function which is not overloaded, so it has type void (a<b>::*)(int) without need for further analysis. Conversion from any pointer-to-member-function to any other pointer-to-member-function is a well-formed reinterpret_cast with unspecified results except if converted back to the original type, and a C-style cast can do what a reinterpret_cast can do.
For template functions we have [over.over]/1-2:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. A function with type F is selected for the function type FT of the target type required in the context if F (after possibly applying the function pointer conversion) is identical to FT. The target can be
...
an explicit type conversion ([expr.type.conv], [expr.static.cast], [expr.cast]),
...
If the name is a function template, template argument deduction is done ([temp.deduct.funcaddr]), and if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization, which is added to the set of overloaded functions considered. [ Note: As described in [temp.arg.explicit], if deduction fails and the function template name is followed by an explicit template argument list, the template-id is then examined to see whether it identifies a single function template specialization. If it does, the template-id is considered to be an lvalue for that function template specialization. The target type is not used in that determination. — end note ]
So this means we first try template argument deduction for a<b>::x<int>, matching it to the target type void (a<b>::*)(). But there are no specializations that can possibly give an exact match, since they all have one argument, not zero, so deduction fails. But per the note, there's also [temp.arg.explicit] (paragraph 3 in C++17, 4 in the latest C++20 draft):
Trailing template arguments that can be deduced or obtained from default template-arguments may be omitted from the list of explicit template-arguments. A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced as an empty sequence of template arguments. ... In contexts where deduction is done and fails, or in contexts where deduction is not done, if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
In testx, the template-id a<b>::x<int> identifies a single function template specialization. So it names that specialization, and again the C-style cast is valid with unspecified result.
So in testva, does a<b>::va<int> identify a single specialization? It would certainly be possible to use that expression to name different specializations, via [temp.arg.explicit]/9:
Template argument deduction can extend the sequence of template arguments corresponding to a template parameter pack, even when the sequence contains explicitly specified template arguments.
Except this says "template argument deduction". And here the template argument deduction involved fails, since it required an impossible match with the target type void (a<b>::*)(). So nothing really explains whether a<b>::va<int> identifies a single specialization, since no other method of getting additional template arguments is described, or identifies multiple specializations, since it could be validly used in other contexts with matching target types.
clang is right
[expr.reinterpret.cast]/10
A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types. The null member pointer value is converted to the null member pointer value of the destination type. The result of this conversion is unspecified, except in the following cases:
Converting a prvalue of type “pointer to member function” to a different pointer-to-member-function type and back to its original type yields the original pointer-to-member value.
Converting a prvalue of type “pointer to data member of X of type T1” to the type “pointer to data member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer-to-member value.
&a<b>::va<int> et al. are prvalue of type "pointer to member of a<b> of type void(int)" and converting it (without calling the resulting function pointer whose value is unspecified) is legal.
Let's simplify to this example:
struct a {
template <typename... Args>
void va(Args...) {}
template <typename X>
void x(X) {}
void y(int) {}
};
using no_args = void(a::*)();
using int_arg = void(a::*)(int);
And lets try the following four things:
reinterpret_cast<no_args>(&MEMBER_FUNCTION); // (1)
(no_args) &MEMBER_FUNCTION; // (2)
(no_args) static_cast<int_arg>(&MEMBER_FUNCTION); // (3)
int_arg temp = &MEMBER_FUNCTION; (no_args) temp; // (4)
(Replacing MEMBER_FUNCTION with &a::va<int>, &a::x<int> and &a::y).
clang compiles all of them.
gcc compiles everything except (2) with &a::va<int> and &a::x<int>.
MSVC compiles everything except (1) and (2) with &a::va<int> (but is fine with &a::x<int>).
Notice that (3) is essentially the same as (4).
https://gcc.godbolt.org/z/a2qqyo showing an example of this.
What you can see from this is that &MEMBER_FUNCTION is not resolved to a specific member function pointer in case of templates, but if it is resolved, it is allowed to reinterpret it into another member function pointer type.
What the standard has to say:
[over.over]/1:
A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set.
A function template name is considered to name a set of overloaded functions in such contexts.
A function with type F is selected for the function type FT of the target type required in the context if F (after possibly applying the function pointer conversion) is identical to FT.
[ Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type.
— end note
 ]
The target can be:
[...]
- an explicit type conversion ([expr.type.conv], [expr.static.cast], [expr.cast])
An example given later on is:
int f(double);
int f(int);
void g() {
(int (*)(int))&f; // cast expression as selector
}
And a few more quotes about templates:
[temp.deduct.funcaddr]/1:
Template arguments can be deduced from the type specified when taking the address of an overloaded function.
The function template's function type and the specified type are used as the types of P and A, and the deduction is done as described in [temp.deduct.type].
[temp.arg.explicit]/4
[...] if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
This seems like MSVC is right.
&a::va<int> is not resolved unless you assign/cast it to a void(a::*)(int). You should also be able to assign it to void(a::*)(int, char) or void(a::*)(int, double, char), where the Args would be deduced as { int, char } and { int, double, char } respectively. That means that (no_args) &a::va<int> should fail, as there are many possible sets of Args it could be (All of them start with int, and clang overzealously resolves it), and none of them take zero parameters, so (no_args) &a::va<int> is a static_cast that should fail.
As for &a::x<int>, there is only one possible function, so it should work exactly the same way as &a::y (But gcc still hasn't resolved it yet).

Conditional operator's return type and two-phase lookup

Consider the following snippet:
struct Base { };
struct Derived : Base { };
void f(Base &) { std::cout << "f(Base&)\n"; }
template <class T = int>
void g() {
Derived d;
f(T{} ? d : d); // 1
}
void f(Derived &) { std::cout << "f(Derived&)\n"; }
int main() {
g();
}
In this case, I reckon that the function call to f at // 1 should be looked up in phase one, since its argument's type is unambigously Derived&, and thus be resolved to f(Base&) which is the only one in scope.
Clang 3.8.0 agrees with me, but GCC 6.1.0 doesn't, and defers the lookup of f until phase two, where f(Derived&) is picked up.
Which compiler is right ?
Using the latest version of the C++ standard Currently n4582.
In section 14.6 (p10) it says the name is bound at the point of declaration if the name is not dependent on a template parameter. If it depends on a template parameter this is defined in section 14.6.2.
Section 14.6.2.2 goes on to say an expression is type dependent if any subexpression is type dependent.
Now since the call to f() is dependent on its parameter. You look at the parameter type to see if it is depending on the type. The parameter is False<T>::value ? d : d. Here the first conditional is depending on the type T.
Therefore we conclude that the call is bound at the point of instantiation not declaration. And therefore should bind to: void f(Derived &) { std::cout << "f(Derived&)\n"; }
Thus g++ has the more accurate implementation.
14.6 Name resolution [temp.res]
Para 10:
If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declarations) for that name shall be in scope at the point where the name appears in the template definition; the name is bound to the declaration (or declarations) found at that point and this binding is not affected by declarations that are visible at the point of instantiation.
14.6.2.2 Type-dependent expressions [temp.dep.expr]
Except as described below, an expression is type-dependent if any subexpression is type-dependent.
I think gcc (and visual studio, by the way) are right on this one.
n4582, §14.6.2.2
Except as described below, an expression is type-dependent if any subexpression is type-dependent.
In T{} ? d : d, there are 3 sub expressions:
T{}, obviously type dependent
d (2 times), not type dependent
Since there is a type dependent sub expression and the ternary operator does not figure in the list of exceptions in §14.6.2.2, it is considered type dependent.
According to c++ draft (n4582) §14.7.1.5:
Unless a function template specialization has been explicitly
instantiated or explicitly specialized, the function template
specialization is implicitly instantiated when the specialization is
referenced in a context that requires a function definition to exist.
Unless a call is to a function template explicit specialization or to
a member function of an explicitly specialized class template, a
default argument for a function template or a member function of a
class template is implicitly instantiated when the function is called
in a context that requires the value of the default argument.
I would say gcc is more correct about this.
If you for example create an specialized version of void g() you get both compiler doing the same.

Instantiation of template function overloads

I'm aware that the compiler will not instantiation unused template functions as long as they are not virtual in a class.
In a simple case, if I have two overloaded template functions both of which take the same template arguments, it seems the compiler instantiates both overloads. I guess this is required so that the compiler can perform overload resolution? Are overloads exempt from the lazy-instantiation rule for function templates? I wasn't able to find the relevant text in the standard. Here is an example:
template<typename T>
void foo(T) {zzz}
template<typename T>
void foo(T*) {}
int main()
{
int* blah;
foo(blah);
}
I would expect no compiler error if the first overload was not instantiated, however I get the error.
Live Sample
It seems as though you're expecting only one of those overloads to be instantiated because only one of them will be called, but the compiler clearly has to instantiate both of them in order to determine whether either of them can be called and, if so, which one to use.
The more formal answer is that both templates are candidates because your T can always be pointerised, so both are "used" in that sense:
[C++14: 14.7.1/3]: Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.
[C++14: 14.7.1/10]: If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated (14.8.3).
So, basically:
I guess this is required so that the compiler can perform overload resolution?
Correct.
Your question, however, already stems from a misconception that your first function template can be ignored: it can't be. zzz does not depend on any template parameters so SFINAE is not involved; even if SFINAE were involved, it could not help you with your invalid syntax. So, no matter what you do, that code is ill-formed:
template<typename T>
void nil() {zzz}
// g++ -c -std=c++11 -O2 -Wall -pedantic -pthread main.cpp
// main.cpp: In function 'void nil()':
// main.cpp:2:13: error: 'zzz' was not declared in this scope
// void nil() {zzz}
// ^
(live demo)
That being said, a diagnostic is not required in this case; in particular, Microsoft Visual Studio has historically silently accepted such code:
[C++14: 14.6/8]: Knowing which names are type names allows the syntax of every template to be checked. No diagnostic shall be issued for a template for which a valid specialization can be generated. If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required. If every valid specialization of a variadic template requires an empty template parameter pack, the template is ill-formed, no diagnostic required. If a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is done, and if the completeness of that type affects whether or not the program is well-formed or affects the semantics of the program, the program is ill-formed; no diagnostic is required. [..]
The same wording may also be found in C++11 and C++03, so this has always been the case. Your misconception is therefore understandable.
Incidentally, your observation regarding virtual functions is also not completely accurate:
[C++14: 14.7.1/11]: An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, or a static data member of a class template that does not require instantiation. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated. The use of a template specialization in a default argument shall not cause the template to be implicitly instantiated except that a class template may be instantiated where its complete type is needed to determine the correctness of the default argument. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated.
This is not an answer from the inner question, but it is related about the assumption of the question: "I would expect no compiler error if the first overload was not instantiated, however I get the error."
Sure? so, why this code generate a compiler error?
template<typename T>
void nil(T) {zzz}
template<typename T>
void foo(T*) {}
int main()
{
int* blah;
foo(blah);
}
because nil is not instantiated