2

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

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
void.pointer
  • 24,859
  • 31
  • 132
  • 243

2 Answers2

6

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.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • FWIW, if it's "unspecified" then I treat it as no guarantee, i.e. don't depend on it. My wording was more pragmatic than correct, sorry for the misunderstanding. – void.pointer Nov 19 '14 at 16:08
  • I did another test with partial specialization of a class, and it surprisingly still tries to instantiate both classes. I'm not sure what I'm doing wrong: http://ideone.com/YUFJp0 – void.pointer Nov 19 '14 at 16:09
  • Actually `T` in my partial specialization could also be a pointer, I'd need a way to specialize on non-pointer types (I think), but not sure how to do that. – void.pointer Nov 19 '14 at 16:14
  • @void.pointer: It's not trying to instantiate anything; that `zzz` is simply ill-formed, regardless of whether the specialisation in which you wrote it is instantiated. – Lightness Races in Orbit Nov 19 '14 at 16:17
  • Ah, Thanks Lightness... I forgot that in C++11 I recall that illformed code is now checked for by the compiler in all template code, whether or not its instantiated. I'm relying on behavior that only existed in C++03 I think. – void.pointer Nov 19 '14 at 16:19
  • @void.pointer: [Nope](http://coliru.stacked-crooked.com/a/b02f8bff1fd7f83d). AFAIK, it's _always_ been this way. – Lightness Races in Orbit Nov 19 '14 at 16:19
  • @void.pointer Thats not true. I used an c++03 compiler and it give me the errors too – Amadeus Nov 19 '14 at 16:19
  • Maybe I'm incorrect, but I do remember years ago using MSVC compilers, obvious compiler errors would not show up until you used template code. Wish I could dig deep enough into my memories to provide more concrete examples. – void.pointer Nov 19 '14 at 16:21
  • 1
    @void.pointer: MSVC compilers have historically implemented C++ rather poorly. I'm interested to hear more about this, though. – Lightness Races in Orbit Nov 19 '14 at 16:21
  • This is a great answer (as always; not sure why you were downvoted BTW), thank you. Apparently the root of the issue here is that I was expecting the compiler to "ignore" ill-formed code if the function was not instantiated. Thanks for the refresher on that, as well as excellent references to the standard on instantiation behavior. – void.pointer Nov 19 '14 at 16:28
  • @LightnessRacesinOrbit This is a question WRT the MSVC behavior for ill-formed code in templates. It may shed some light on what behavior I witnessed: http://stackoverflow.com/questions/10323980/templates-compilation-gcc-vs-vs2010 – void.pointer Nov 19 '14 at 16:43
  • @void.pointer: Yep, that's the one. Thanks for your research. – Lightness Races in Orbit Nov 19 '14 at 16:51
  • @void.pointer: I've now integrated that knowledge into my answer. – Lightness Races in Orbit Nov 19 '14 at 16:58
2

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

Amadeus
  • 10,199
  • 3
  • 25
  • 31