Difference between explicit specialization and regular functions when overloading a template function - c++

I'm on a roll today. Here goes n00b question number 7:
What's the difference between explicit specialization and just regular functions when you try to overload a template function?
What's the appropriate situation to use the explicit specialization? I don't quite understand it:
#include <iostream>
template <typename s> void test(s var1);
template <> void test<int>(int var1);
int main(){
test(1);
test(1.1);
test("hello!!");
return 0;
}
template <typename s> void test(s var1){
std::cout << var1 << std::endl;
}
template <> void test<int>(int var1){
std::cout << "int " << var1 << std::endl;
}
As oppose to:
#include <iostream>
template <typename s> void test(s var1);
void test(int var1);
int main(){
test(1);
test(1.1);
test("hello!!");
return 0;
}
template <typename s> void test(s var1){
std::cout << var1 << std::endl;
}
void test(int var1){
std::cout << "int " << var1 << std::endl;
}

There really isn't a difference between an explicitly specialized template function and a non-template regular function other than the fact that when the compiler looks for a matching signature type for the function call, it will first pick a non-template function that matches the required signature before trying to instantiating any available template functions that may fulfill the required signature match.
If you are going to declare and define a function inside a header file that is not a template-function though, you will have to declare the function as inline. That is because a template function is not an actual function that is linked with a code module until it is actually instantiated. The linker then throws away that instantiation after compiling the code module. If the linker did not do this, then every time a .cpp file included the header file, the linker would complain about duplicate definitions for a function. Using the inline keyword on a non-template function has a similar effect at the compiler level, in that any time the function is used in a .cpp file, the compiler replaces that function call with the body of the function code from the inline function in the header file, and avoids the overhead of a function call with an associated stack active record setup and clean-up. Therefore the linker won't complain about duplicate definitions for a function.

I'm not an expert, but my experience is to use templates (and specialization) when I want to define different return types. You can't overload the return type of a function.

A major difference is: Explicit specializations don't participate in overloading at all.
template<typename T> void f(T const&);
template<> void f<char const*>(char const * const&);
Calling with f("hello") will not consider any explicit specializations. It will only take all templates, and deduce their template arguments. The above T will be deduced to char[6], and so the specialization won't be selected.
If you overload the function template instead, you have completely different characteristics.
template<typename T> void f(T const&);
void f(char const * const&);
Calling this, it will select the second function, because both the (char const(&)[6]) parameter of the generated specialization and the (char const * const&) parameter match the argument equally well, but the second function is a non-template function, hence it is preferred eventually.

When the compiler comes across a function call, it first looks for a non-template function definiton, then an explicitly specialized template and finally, a template definition whose signature matches with the function call.
So, for example, if you have an explicitly defined template and a template, then the complier goes for the explicitly defined template. Therefore, you should use an explicity specialzed template when you want to handle a particular datatype in a different manner from the way the template would have handled it.
Another use for explicit specialization is when you want to "overload" a function that does not take any arguments. You can use explicit specialization to give different definitions to the function to handle different datatypes.

IMHO, explicit specialization for function template should be used when, you are going to call that function using explicit template argument. e.g.
test<int>(myClass); // no argument for 'int' -> declare test() as template specialization
In other cases, you should always use normal specialization. e.g.
test(10, myClass); // argument 10 is 'int' -> test() should be normal specialized
Technically there is no difference between normal and explicit template specialization of a function. The normal specialized versions are completely independent of template functions.

Related

Explicit template instantiation and forwarding references

Recently, I was discussing with students the possibilities of restriction of types for templates that uses forwarding references. I knew about comparing types by is_same together with static_assert or enable_if, but we also talked about explicit template instantiation.
The following example works for me with GCC:
f.h:
template <typename T>
void f(T&& param); // declaration
f.cpp:
#include <iostream>
template <typename T>
void f(T&& param) { std::cout << param << std::endl; }
// explicit instantiations:
template void f<int>(int&&);
template void f<int&>(int&);
main.cpp:
#include "f.h"
int main() {
f(1);
// f('1'); // LINKER ERROR
int i = 2;
f(i);
}
I am not an expert in explicit template instantiations, so I just wonder whether such a solution is portable/standard-compliant. (Please, do not ask me about use cases, I don't have any. For me, it's purely an academic question.)
UPDATE
I am also a bit confused about the format of the explicit instantiation (<int>(int&&) and <int&>(int&)), but I guess that it is given by template deduction and maybe reference collapsing rules.
This is okay and it is portable/standard-compliant.
This is called template specialization and you can read more on this topic here
Template specialization is writing a specific handler for a specific invoke.
In your code, you have two specializations.
The first receives a rvalue reference(e.g. integer literal like 5) and returns an int.
The second one receives a lvalue reference(e.g. the variable b who is of type int an have a value of 5) and returns a lvalue reference.
In the default case, you're trying to print the param using std::stringstream.

Templates vs Similar Non-Template Functions

I had created a Template Specialized function and a non template function with the same arguments. Since effectively both are same functions, I was not sure how C++ compiler would run it as now it has two same functions, one is a template specialized the other is a non template one. I was expecting this would result in a compiler error as compiler would find two functions with the same parameters and return type (In this case void foo(string) ). But looks like the non template version is the one that executes when this is called.
So is there a precedence when this is done?
Kindly let me know if I have misunderstood.
Code: This prints 'String Non-Template'
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void foo(T input)
{
cout <<"Generic Template"<<endl;
}
template<>
void foo<string>(string input)
{
cout <<"String Template"<<endl;
}
void foo(string input)
{
cout <<"String Non-Template"<<endl;
}
int main() {
string input = "abc";
foo(input);
return 0;
}
A regular function is always preferred over a function template, even a specialization of the function template.
From the C++ Standard:
13.3.3 Best viable function [over.match.best]
1 Define ICSi(F) as follows:
...
Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
...
-- F1 is a non-template function and F2 is a function template specialization, or, if not that,

C++ function template specialization declarations and template arguments; none vs. <> vs. <type>

When studying function templates, I see specializations declared in different ways:
template<> void f(argtype) {}
template<> void f<>(argtype) {}
template<> void f<argtype>(argtype) {}
... and I wonder about the differences between these. Given the below example with template functions with and without parameter, I have a few questions.
#include <iostream>
#include <typeinfo>
//Function print1 WITH function parameter---------------------------------------------
template<class T>
void print1(T) { std::cout << "Primary template for print1() with type " << typeid(T).name() << std::endl; }
template<>
void print1<int>(int) { std::cout << "Specialization for print1<int>(int)" << std::endl; }
//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1<>(int) { std::cout << "Specialization for print1<>(int)" << std::endl; }*/
//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1(int) { std::cout << "Specialization for print1(int)" << std::endl; }*/
//Function print2 WITHOUT function parameter------------------------------------------
/*Not allowed together with print<>(); compiler complains:
t2.cpp:29:6: error: template-id 'print2<>' for 'void print2()' does not match any template declaration*/
/*template<class T>
void print2() { std::cout << "Primary template for print2()" << std::endl; }*/
template<class T = short> //Declaration of print2<>() now ok in conjunction with print2<>()
void print2() { std::cout << "Primary template for print2()" << std::endl; }
template<>
void print2<int>() { std::cout << "Specialization for print2<int>()" << std::endl; }
template<>
void print2<>() { std::cout << "Specialization for print2<>()" << std::endl; }
int main() {
//These three work in the same way, no matter which call method we use, so far so good
print1(10);
print1<>(10);
print1<int>(10);
print1(true);
print1<>(true);
print1<bool>(true);
print2(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
print2<>(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
print2<bool>(); //Triggers print2<bool>() primary template
print2<short>(); //Triggers print2<>(), should definately trigger primary template for print2()
print2<int>(); //Triggers print2<int>() specialization
return 0;
}
outputs:
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Primary template for print1() with type b
Primary template for print1() with type b
Primary template for print1() with type b
Specialization for print2<>()
Specialization for print2<>()
Primary template for print2()
Specialization for print2<>()
Specialization for print2<int>()
What special meaning is derived from leaving the template specialization argument empty, non-existent or with the specialized type and how does it effect the outcome?
It seems that with a function argument, this specification is superfluous and the compiler deduces it no matter how it's specified (with the result that equivalent explicit specifications become unallowed redeclarations).
I understand that given a function without parameters, the specialized template argument is needed explicitly in declaration to specify for which instantiation the defined function
applies to (since it can't be deduced otherwise). But the meaning seems to imply something more in this case and the "empty" specialization (<>) is triggered in a somewhat unforeseen ways. How come?
Why do I have to have a default template parameter when specializing print2 with print2<>() but not without it?
What special meaning is derived from leaving the template
specialization argument empty, non-existent or with the specialized
type and how does it effect the outcome?
If you do provide a template argument list completely then you're simply explicitly specializing the function template for a given set of template arguments.
If you provide arguments for a (possibly empty) subset of the template parameters then you are explicitly specializing the function template for a set of arguments that has to be deduced. According to [temp.deduct.decl]:
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 (14.7.2),
explicit specializations (14.7.3), and certain friend declarations
(14.5.4). […]. In all these cases, P is the type of the function
template being considered as a potential match and A is […] the
function type from the declaration […]. The deduction is done as
described in 14.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 (14.5.6.2), deduction fails and, in the declaration cases,
the program is ill-formed.
So for every parameter for which no argument was given, or in the case where no list is specified at all, template argument deduction is done for each corresponding parameter of the specialization and its counterpart from the primary template. The process is described in §14.8.2.5 and works just as if we called the primary template with the provided template argument list as the template arguments and objects of the types of the parameters in the specialization as the function arguments. You should be familiar with the fact that one can call a function template with some of the template arguments specified, e.g.
template <typename A, typename B> void foo(A, B);
foo(7684, 48.);
foo<int>(7684, 48.);
foo<int, double>(7684, 48.);
That works equivalently for explicit specializations:
template <typename T, typename U>
void foo(T, U) {}
// Both template arguments have to be deduced.
template<> void foo(double, float);
// The *exact* same as above.
// template<> void foo<>(double, float);
// Second template argument has to be deduced by type.
// If we call foo<int>(int(), float()) then the deduced specialization is
// foo<int, float>, thus U=float.
template<> void foo<int>(int, float);
template<> void foo<int, int>(int, int);
This can also be applied to overloads of a function template. In an attempt to find the primary template a specialization is corresponding to, the most specialized one is chosen.
template <typename T, typename U>
void foo(T&, U&) {}
template <typename T, typename U>
void foo(T const&, U&) {}
// Specializes the second overload because it is more specialized.
template <>
void foo(int const&, float&);
Note that while looking for a primary template, the arguments provided (i.e. not to be deduced) are used to check the resulting function parameter of the primary template against the resulting function parameter of the specialization. They have to be equal.
template <typename T, typename U>
void foo(T&, U&) {}
// Error - no matching primary template found.
template <>
void foo<int, int>(float&, int&);
// Dito:
template <>
void foo<int>(int, int&);
It seems that with a function argument, this specification is
superfluous and the compiler deduces it no matter how it's specified
(with the result that equivalent explicit specifications become
unallowed redeclarations).
Yes, that is indeed the case. Consider that if you specify a template argument invalidly that results in an error:
But the meaning seems to imply something more in this case and the
"empty" specialization (<>) is triggered in a somewhat unforeseen
ways. How come?
For a call, the template arguments are deduced first. Then the specialization with those template arguments is called.
If you explicitly specialized a function template for this particular specialization, here that is print2<> which is print2<short>, then that explicit specialization is thus called. In what way is that unforeseen?
Why do I have to have a default template parameter when specializing
print2 with print2<>() but not without it?
Because the compiler cannot deduce the argument. If you provide a default argument he doesn't have to deduce it in the first place.
What special meaning is derived from leaving the template specialization argument empty
Missing arguments are deduced if possible; an empty argument list means that all arguments are to be deduced.
non-existent
That means you're declaring the primary template, not an explicit specialisation.
or with the specialized type
That means you're declaring an explicit specialisation for that type.
the "empty" specialization (<>) is triggered in a somewhat unforeseen ways. How come?
In both cases, the template argument is deduced. In print1, the primary template declares that T is the same type as the function parameter; so it's deduced from the function parameter. In print2, it's declared with a default type of short, so that is used. So your surprise at seeing print2<> when you think it should be print2<short> is explained: print2<> is print2<short>.
Why do I have to have a default template parameter when specializing print2 with print2<>() but not without it?
If there were neither a default argument nor a function parameters from which to deduce an argument, then it would be impossible to deduce a type for the specialisation, so <> couldn't be used. I don't know what you mean by "without it"; if you mean "without <>", then you're declaring the primary template, not a specialisation, and the parameter is generic.

C++ overload function by return type

If I thought I knew anything about C++ then it was that you can't overload functions by return type.
So can anyone explain what is going on here please?
class A { public: typedef int _foo; };
class B {};
template<class T>
typename T::_foo Foo(int)
{
cout << "Foo(int)\n"; return typename T::_foo();
}
template<class T>
typename T Foo(char)
{
cout << "Foo(char)\n"; return typename T();
}
int main()
{
Foo<A>(0); // Writes "Foo(int)", as expected.
Foo<B>(0); // Writes "Foo(char), expected error unable to compile template.
return 0;
}
There are two classes A and B. A defines typedef _foo, B does not. There are two overloads of function template Foo, Foo(int) and Foo(char). Foo(int) returns T::_foo, Foo(char) returns T.
Foo(0) is then called twice. This is an exact match for Foo(int) so I would expect Foo<A>(0) to compile ok, and Foo<B>(0) to fail to compile since B does not define the type _foo used in the template.
What actually happens is that Foo<B>(0) completely ignores Foo(int) and instantiates Foo(char) instead. But by the normal rules of overload resolution Foo(0) is clearly an exact match for Foo(int), and the only thing that makes Foo(char) a more viable match is the return type which should not be considered.
To verify that it is the return value that is affecting the overload resolution just add this:
template<class T>
void Bar(int) { typename T::_foo a; cout << "Bar(int)\n"; }
template<class T>
void Bar(char) { cout << "Bar(char)\n"; }
Bar<A>(0); // Writes "Bar(int), as expected.
//Bar<B>(0); // Error C2039: '_foo' : is not a member of 'B', as expected.
This makes it clear that in the absence of the return value Foo(int) is indeed the correct overload, and that if the template cannot resolve the types used from its template argument that failure to compile is the normal outcome.
You're not overloading on return type, you're specializing a function template and when the Foo<B>(int) specialization forms the invalid type B::_foo that specialization is removed from the overload set by SFINAE, leaving the Foo<B>(char) function as the only viable function.
In more detail, the call to Foo<A>(0) first performs name lookup to find all the Foo names in scope, then instantiates any function templates to find the overload candidates, then overload resolution chooses the best match.
The step of instantiating the function templates produces these two function declarations:
int Foo<A>(int);
A Foo<A>(char);
Overload resolution chooses the first one as the best match.
However when calling Foo<B>(0) the instantiations produce these declarations:
<invalid type> Foo<B>(int);
B Foo<B>(char);
The first declaration is not valid, so there is only one candidate for overload resolution, so that is the one that gets called.
In your Bar example the invalid type that gets formed during instantiation is not in "the immediate context" of the function declaration (it's in the function definition i.e. body) so SFINAE does not apply.
template<class T>
typename T::_foo Foo(int);
template<class T>
typename T Foo(char);
So your code declares this overloaded function. That's nice.
Foo<A>(0);
In this case, the compiler tries to fill out the template for the prototypes declared above, which would be:
int Foo(int);
A foo(char);
And since you're passing an integer as a parameter, the first is a better match, so the compiler uses that one.
Foo<B>(0);
Again the compiler sees this line and tries to fill out the template for the prototypes, but...
WTFDOESNTMAKESENSE?!?!? Foo(int);
A foo(char);
So clearly, the first one doesn't even make sense, so it discards that and uses the second overload. This actually has nothing to do with return types, it has to do with how template prototypes are filled out before it decides which function you mean. Here's your example rearranged to clarify:
template<class T>
int foo(T::_foo) {}
template<class T>
int foo(char) {}
int main() {
foo<A>(0); //uses the first, `int foo(int)` better than `int foo(char)`
foo<B>(0); //uses the second, because the first doesn't work with B.
This is called SFINAE, and note that it only works in very particular circumstances in template parameters, return types, and function parameters, but not the function body itself. This is why your "verification" caused an error, because it can't tell that one of the functions is invalid from the prototype, and the prototype is the only thing considered when it's deciding between overloads.

function template specialization failed?

#include <iostream>
template <class T>
void foo(T) {
std::cout << "foo(T)" << std::endl;
}
template <class T>
void foo(T*) { //#3
std::cout << "foo(T*)" << std::endl;
}
#define TEST
#ifdef TEST
template <>
void foo(int*) { //#1
std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
std::cout << "foo<int*>(int*)" << std::endl;
}
#endif
int main(int argc, char **argv) {
int* p = 0;
foo(p);
return 0;
}
What is the difference between #1 and #2 above? If I define TEST, then #1 works, but if I comment it out, #3 works. Which is the right way to write function template specialization here?
#1 declares a function template specialization of #3 and automatically deduces the template parameters. #2 is a specialization of the first template you defined (the one without number, let's call it #0) for T=int*. It can't be a specialization of #3 because replacing T with the specified int* there would lead to a int** parameter.
When you call foo, overload resolution now first picks the best fitting base template, then checks that template for any existing specializations. With TEST defined, there are two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, and #1 is a better fit and is being called.
Without TEST defined, there are still two base templates (#0 and #3) and #3 is a better match and gets selected. Then the compiler checks for specializations of that template, but since #2 specializes #0 and not #3, it is not considered and #3 ends of being called.
This is the classical example of Why not Specialize Function Templates. The problems are explained in more detail there.
The simple solution is to not specialize function templates at all, but simply add new overloads for the special types:
// no template, just a normal function
void foo(int*) {
std::cout << "foo(int*)" << std::endl;
}
For function template specializations you can explicitly list the template arguments but you don't have to if the template arguments are deduced. If you don't specify the template arguments, they are deduced by the compiler using the same rules as overload resolution. For deciding which function overload is to be chosen, the compiler starts with looking only at the primary templates (which are selected by some magical process in the first place). Looking at the two available primary templates
template <typename T> void foo(T);
template <typename T> void foo(T*);
the latter is a better match for a pointer argument. Once the proper primary template is found, the compiler looks for potential specializations of this primary template. However, your example #2 actually is not a specialization of function template taking a pointer argument although it involves a pointer argument. If you take the primary declaration
template <typename T> void foo(T*);
and you replace T by the explicitly specified template argument int* you get
template <> void foo<int*>(int**);
That is, the declaration
template <> void foo<int*>(int*);
is something different. You probably just want to lose the pointer when specifying the template argument:
template <> void foo<int>(int*);
I can't really tell which function #2 is supposed to specialize, or exactly how the extremely complicated overload resolution rules would select the function to call.
I do know that you most often don't need to specialize functions, but can rely on overloading instead. To get a function for int* you just need
void foo(int*) {
std::cout << "foo(int*)" << std::endl;
}
A non-template function will be preferred over templates, as long as the parameter matches.