I found out that you can specialize a template after it's first use if you use it using a wrapper template. Simple example:
#include <iostream>
template<typename T>
const char* templateImpl();
template<typename T>
const char* templateGetter() { return templateImpl<T>(); }
struct S{};
int main(){ std::cout << templateGetter<S>() << std::endl; return 0; }
template<>
const char* templateImpl<S>(){ return "S"; }
This works with every compiler - I'm not surprised MSVC compiles it since it handles templates differently, but GCC and clang allow it too. I thought the standard required the specialization to occur before the first use, which in this case would mean before main and expected them to report an error.
Did I miss something, is this code standard compliant?
To clarify, if I change templateGetter<S>
to templateImpl<S>
in main, the program won't compile with the error message I would expect from this too:
main.cpp:14:29: error: specialization of 'const char* templateImpl() [with T = S]' after instantiation
This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming. Generic programming is an approach where generic data types are used as parameters in algorithms so that they work for variety of suitable data types.
An explicit specialization of a function template is inline only if it is declared with the inline specifier (or defined as deleted), it doesn't matter if the primary template is inline.
It allows you to define the generic classes and generic functions and thus provides support for generic programming. Generic programming is a technique where generic types are used as parameters in algorithms so that they can work for a variety of data types. Templates can be represented in two ways: Function templates.
There are two types of templates in C++, function templates and class templates.
You got (un)lucky. This is ill-formed NDR.
[temp.expl.spec]/6-7:
6 If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. [...]
7 The placement of explicit specialization declarations for function templates, class templates, variable templates, member functions of class templates, static data members of class templates, member classes of class templates, member enumerations of class templates, member class templates of class templates, member function templates of class templates, static data member templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, static data member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, variable templates, member class templates of non-template classes, static data member templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.
p7 isn't really useful here, but I can't resist quoting it :)
Instantiating templateGetter<S>
causes the implicit instantiation of a declaration of templateImpl<S>
. You didn't see an error with your code because many implementations like to defer template instantiations until the end of the translation unit when possible, which is a permitted implementation technique. (I'm not going to quote the standardese here, but you'll find that function template specializations have an extra point of instantiation at the end of the translation unit.)
Giving templateGetter
a deduced return type will force early instantiation of its body:
template<typename T>
auto templateGetter() { return templateImpl<T>(); }
and voila:
+ g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:29: error: specialization of 'const char* templateImpl() [with T = S]' after instantiation
const char* templateImpl<S>(){ return "S"; }
^
+ clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:13: error: explicit specialization of 'templateImpl<S>' after instantiation
const char* templateImpl<S>(){ return "S"; }
^
main.cpp:7:32: note: implicit instantiation first required here
auto templateGetter() { return templateImpl<T>(); }
^
1 error generated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With