A textbook I have notes that you can provide your own implementation for standard library functions like swap(x,y) via template specialization or function overloading. This would be useful for any types which can benefit from something other than an assignment swap, like STL containers for example (which already have swaps written, I know).
My questions are the following:
What's better: template specialization to give your specialized
swap implementation, or function overloading providing the exact
parameters you wish to use without a template?
Why is it better? Or if they're equal, why is this?
Short story: overload when you can, specialise when you need to.
Long story: C++ treats specialisation and overloads very differently. This is best explained with an example.
template <typename T> void foo(T);
template <typename T> void foo(T*); // overload of foo(T)
template <> void foo<int>(int*); // specialisation of foo(T*)
foo(new int); // calls foo<int>(int*);
Now let's swap the last two.
template <typename T> void foo(T);
template <> void foo<int*>(int*); // specialisation of foo(T)
template <typename T> void foo(T*); // overload of foo(T)
foo(new int); // calls foo(T*) !!!
The compiler does overload resolution before it even looks at specialisations. So, in both cases, overload resolution chooses foo(T*). However, only in the first case does it find foo<int*>(int*) because in the second case the int* specialisation is a specialisation of foo(T), not foo(T*).
You mentioned std::swap. This makes things even more complicated.
The standard says that you can add specialisations to the std namespace. Great, so you have some Foo type and it has a performant swap then you just specialise swap(Foo&, Foo&) in the std namespace. No problems.
But what if Foo is a template class? C++ doesn't have partial specialisation of functions, so you can't specialise swap. Your only choice is overloading, but the standard says that you aren't allowed to add overloads into the std namespace!
You have two options at this point:
Create a swap(Foo<T>&, Foo<T>&) function in your own namespace, and hope that it gets found via ADL. I say "hope" because if the standard library calls swap like std::swap(a, b); then ADL simply won't work.
Ignore the part of the standard that says not to add overloads and do it anyway. Honestly, even though it's technically not allowed, in all realistic scenarios it's going to work.
One thing to remember though is that there's no guarantee that the standard library uses swap at all. Most algorithms use std::iter_swap and in some implementations that I've looked at, it doesn't always forward to std::swap.
There's little to add to Peter Alexander's answer. Let me just mention one use in wich function specialization could be prefearable over overloading: if you have to select among functions with no parameters.
E.g.
template<class T> T zero();
template<> int zero() { return 0; }
template<> long zero() { return 0L; }
To do something similar using function overloading, you would have to add a parameter to the function signature:
int zero(int) { return 0; }
long zero(long) { return 0L; }
You aren't allowed to overload functions in the std namespace, but you are allowed to specialize templates (as I recall), so that's one option.
The other option is to put your swap function in the same namespace as the thing it's operating on and using std::swap; before calling an unqualified swap.
Related
A textbook I have notes that you can provide your own implementation for standard library functions like swap(x,y) via template specialization or function overloading. This would be useful for any types which can benefit from something other than an assignment swap, like STL containers for example (which already have swaps written, I know).
My questions are the following:
What's better: template specialization to give your specialized
swap implementation, or function overloading providing the exact
parameters you wish to use without a template?
Why is it better? Or if they're equal, why is this?
Short story: overload when you can, specialise when you need to.
Long story: C++ treats specialisation and overloads very differently. This is best explained with an example.
template <typename T> void foo(T);
template <typename T> void foo(T*); // overload of foo(T)
template <> void foo<int>(int*); // specialisation of foo(T*)
foo(new int); // calls foo<int>(int*);
Now let's swap the last two.
template <typename T> void foo(T);
template <> void foo<int*>(int*); // specialisation of foo(T)
template <typename T> void foo(T*); // overload of foo(T)
foo(new int); // calls foo(T*) !!!
The compiler does overload resolution before it even looks at specialisations. So, in both cases, overload resolution chooses foo(T*). However, only in the first case does it find foo<int*>(int*) because in the second case the int* specialisation is a specialisation of foo(T), not foo(T*).
You mentioned std::swap. This makes things even more complicated.
The standard says that you can add specialisations to the std namespace. Great, so you have some Foo type and it has a performant swap then you just specialise swap(Foo&, Foo&) in the std namespace. No problems.
But what if Foo is a template class? C++ doesn't have partial specialisation of functions, so you can't specialise swap. Your only choice is overloading, but the standard says that you aren't allowed to add overloads into the std namespace!
You have two options at this point:
Create a swap(Foo<T>&, Foo<T>&) function in your own namespace, and hope that it gets found via ADL. I say "hope" because if the standard library calls swap like std::swap(a, b); then ADL simply won't work.
Ignore the part of the standard that says not to add overloads and do it anyway. Honestly, even though it's technically not allowed, in all realistic scenarios it's going to work.
One thing to remember though is that there's no guarantee that the standard library uses swap at all. Most algorithms use std::iter_swap and in some implementations that I've looked at, it doesn't always forward to std::swap.
There's little to add to Peter Alexander's answer. Let me just mention one use in wich function specialization could be prefearable over overloading: if you have to select among functions with no parameters.
E.g.
template<class T> T zero();
template<> int zero() { return 0; }
template<> long zero() { return 0L; }
To do something similar using function overloading, you would have to add a parameter to the function signature:
int zero(int) { return 0; }
long zero(long) { return 0L; }
You aren't allowed to overload functions in the std namespace, but you are allowed to specialize templates (as I recall), so that's one option.
The other option is to put your swap function in the same namespace as the thing it's operating on and using std::swap; before calling an unqualified swap.
For illustration purposes, say I have this contrived container:
template <typename T>
struct Container {
T v;
};
And this contrived "value:"
struct Value {};
I have a recursive templated function declared exactly like so:
template <typename T>
void visit(const Container<T> &c) {
visit(c.v);
}
template <typename T>
void visit(const T &v) {}
With the above, it is perfectly valid to invoke visit(Value{}). I can also invoke:
void this_works_too(Container<Value> c) {
visit(c);
}
This is a little puzzling, because the recursive call visit(c.v) occurs before its definition.
But, when we try the following, we get an error (both from clang 6 and gcc 8):
void this_blows_up(Container<int> c) {
visit(c);
}
It complains about the recursive call visit(c.v) now:
error: call to function 'visit' that is neither visible in the
template definition nor found by argument dependent lookup
However, if we reorder the declarations of visit:
template <typename T>
void visit(const T &c) {}
template <typename T>
void visit(const Container<T> &c) {
visit(c.v);
}
Both, this_works_too and this_blows_up compile, successfully.
(This behavior also happens for STL containers and is irrespective of the const and reference qualifiers)
Why does the order become important for specializing visit(Container<int>) when it wasn't for visit(Container<Value>)?
In my research, I had a suspicion that this had to deal with ADL (which I admit I do not fully understand). But my best interpretation of this is, at the end of searching in the case of Container<int>, the set should have found int due to ordinary unqualified lookup. So, both Container<int> and Container<Value> should work.
Godbolt demonstrating this
I have confirmed that despite the reordering, the correct overload is called (ie. when visit(T) is first, visit(Container<T>) is actually invoked for visit(Container<int>) instead of just choosing the more generic visit(T)). I believe this is because the most specific specialization is chosen.
In my research, I had a suspicion that this had to deal with ADL (which I admit I do not fully understand).
You're right that this is the problem.
But my best interpretation of this is, at the end of searching in the case of Container<int>, the set should have found int due to ordinary unqualified lookup.
Ordinary unqualified lookup cannot see the template <typename T> void visit(const T &v) overload because it hasn't been declared yet. Argument-dependent lookup is an exception to that; it can see all declarations at the point of instantiation.
The type int is a built-in type not related to any particular namespace. Because int does not have any associated namespaces, there are no namespaces in which ADL searches for declarations, so there is no way for the second declaration to be found.
The type Value is a user-defined type in the global namespace. Because of that, ADL searches in the global namespace for declarations, and in that case, it does find the second declaration.
I wonder why template specializations make sense?
Aren't the following things equivalent?
Template specialization:
template <typename T>
void f(T t) {
something(t);
}
template <>
void f<int>(int t) {
somethingelse(t);
}
Non-template function instead of specialization:
void f(int t) {
somethingelse(t);
}
I believe these are the same because the non-template function will always be preferred.
This is the answer I came up with:
It's different if the template parameter is not a parameter of the function being defined:
template <typename T>
void f() {
T t;
something(t);
}
template <>
void f<int>() {
int t;
somethingelse(t);
}
In this case defining:
void f() {
int t;
somethingelse(t);
}
would make all the template versions unuseable.
Maybe somebody else has better ideas. :)
The question boils down to determining when the specialization will be used that the overload cannot. There are different situations where this is the case although they are uncommon enough, and it is simple enough to make mistakes that the general recommendation is to prefer overloads to specializations.
When the caller explicitly requests the use of a template. In the code example you provide if the call is f<int>(42) or even f<42>(), then the overload will not be used.
When you cannot provide the required overloads, or the overload cannot be resolved at the place of call. For example if the type is not one of the function arguments (it is either not present in the signature at all or only in the return type:
template
T f();
In this case, you cannot provide overloads int f(); and double f(); but you can provide as many template specializations as you need, and it will be up to the user to force the selection of one or the other. Note that this could be considered a subcase of the previous case: because the template arguments take no part in the function arguments, the user needs to provide the template arguments, so the call is explicitly to a template.
When you want to place special constraints on the combination of arguments and inhibit implicit conversions:
template
void f( T, T ); // Both argument must be the same type
Because template argument deduction only perform perfect matches, this template can only be used when both arguments are of the exact same type, if you add an overload void f(int,int) that overload can be used with any combination of types that are implicitly convertible to int, like f( 5, 3.0 ), but the specialization won't.
In general, for most cases, none of the cases above really apply, so an overload should be preferred.
There might be more, but those are the ones I can recall off the top of my head
The way you declare the function does matter if you insist on calling it like f<int>(42). This will find the specialization, but not the overload.
If the call always looks like f(42), either alternative will work.
Function template specialization is deprecated in favor of function overloads with one exception: you are allowed to add a function template specialization to the std namespace, you aren't allowed to add a new function. So, if you need to supply a specific version for something in the std namespace, you have to use template specialization. For instance, to support creating an unordered_map with a user-defined class as the key you have to specialize std::hash for your class.
Look at the following test code:
template<class T> struct Wrap {};
template<typename T> inline
void fun (T *&Int) // **choice 1**
{}
template<typename T> inline
void fun (Wrap<T> *&Int) // **choice 2**
{}
int main()
{
int i = 6;
fun((char*&)(i)); // **call 1**
fun((Wrap<char>*&)(i)); // **call 2**
}
When I run this code in linux g++, it works as per expectations. When fun() is called with char*&, it calls function of choice 1 straight forward.
However, I am interested when we call fun() with Wrap< char >*& and it calls the choice 2. Even though choice 1 and 2 both appear valid for the 2nd call, compiler manages to choose little better contender -> choice 2 (because it exists).
Question: Is it guaranteed that, the same behavior will be retained for any other compiler for C++ ? If not, then is there any other alternative to make it deterministic?
The second choice is chosen because it's more specialized than the first- that is, T*& can bind to any non-temporary T*, but Wrap<T>*& can only bind to a non-temporary Wrap<T>*. This is Standard as far as I know and should be portable behaviour but what is and isn't portable in practice when it comes to this sort of thing is often not the definition of what's Standard.
Someone with a better knowledge of the spec can confirm this, but I believe that as Wrap<T> is a more specific type than simply T, call 2 will always resolve to 'choice 2', on all platforms compilers.
While the code might look like a template specialization, that is not the case. The language does not allow for partial template function specializations. The two are unrelated templates that happen to be overloads.
The compiler will lookup up the call to fun( (Wrap<char>*&) i ) with the usual lookup mechanisms, will find the two templates and will determine that there are two potential overloads:
template <typename T> void fun( T*& ); // with T == Wrap<char>
template <typename T> void fun( Wrap<T>*& ) // with T == char
Overload resolution will then determine that the second is a better match and instantiate it. This is guaranteed by the standard, but beware: they are not the same template, but rather different templates and you might run into undexpected results. Look at the article #LiKao linked for more insight.
One thing that might make this even more problematic is, that the rules for specializing template classes and template functions differ a lot. This is do to the possibility of overloading template functions while there is no possibility of overloading classes. Because I am not so firm on this topic I will just link to someone who is able to explain it in more depth:
Herb Sutter: "Why Not Specialize Function Templates?"
Learning C++, came upon function templates. The chapter mentioned template specialization.
template <> void foo<int>(int);
void foo( int );
Why specialize when you can use the second? I thought templates were suppose to generalize. What's the point of specializing a function for a specific data type when you can just use a regular function?
Obviously, template specialization exists for a reason. When should it be used? I read Sutter's "Why not Specialize..." article but I need more of a layman's version since I'm just learning this stuff.
The main difference is that in the first case you are providing the compiler with an implementation for the particular type, while in the second you are providing an unrelated non-templated function.
If you always let the compiler infer the types, non-templated functions will be preferred by the compiler over a template, and the compiler will call the free function instead of the template, so providing a non-templated function that matches the arguments will have the same effect of specializations in most cases.
On the other hand, if at any place you provide the template argument (instead of letting the compiler infer), then it will just call the generic template and probably produce unexpected results:
template <typename T> void f(T) {
std::cout << "generic" << std::endl;
}
void f(int) {
std::cout << "f(int)" << std::endl;
}
int main() {
int x = 0;
double d = 0.0;
f(d); // generic
f(x); // f(int)
f<int>(x); // generic !! maybe not what you want
f<int>(d); // generic (same as above)
}
If you had provided an specialization for int of the template, the last two calls would call that specialization and not the generic template.
I personally can see no benefit from specializing a function template. Overloading it by either a different function template or a non-template function is arguably superior because its handling is more intuitive and it's overall more powerful (effectively by overloading the template, you have a partial specialization of the template, even though technically it's called partial ordering).
Herb Sutter has written an article Why not specialize function templates? where he discourages specializing function templates in favour of either overloading them or writing them so that they just forward to a class template's static function and specializing the class template instead.
You can use specialization when you know for a specific class the generic method could be efficient.
template<typename T>
void MySwap(T& lhs, T& rhs)
{
T tmp(lhs);
lhs = rhs;
rhs = tmp;
}
Now for vectors my swap will work, but is not very effecient. But I also know that std::vector implements its own swap() method.
template<>
void MySwap(std::vector<int>& lhs,std::vector<int>& rhs)
{
lhs.swap(rhs);
}
Please don;t compare to std::swap which is a lot more complex and better written. This is just an example to show that a generic version of MySwap() will work but is may not always be efficient. As a result I have shown how it can be made more efficient with a very specific template specialization.
We can also of course use overloading to achieve the same effect.
void MySwap(std::vector<int>& lhs,std::vector<int>& rhs)
{
lhs.swap(rhs);
}
So the question if why use template specialization (if one can use overloading). Why indeed. A non template function will always be chosen over a template function. So template specialization rules are not even invoked (which makes life a lot simpler as those rules are bizarre if you are not a lawyer as well as a computer programmer). So let me thing a second. No can't think of a good reason.
I find it very important. You can use this as you would use a virtual method. There would be no point in virtual methods unless some of them were specialized. I have used it a lot to differentiate between simple types (int,short,float) and objects, object pointers and object references.
An example would be serialize/unserialize methods that would handle objects by calling the objects serialize/unserialize method, while simple types should be written directly to a stream.
One case for template specialization which is not possible with overloading is for template meta-programming. The following is real code from a library that provides some of it services at compile time.
namespace internal{namespace os{
template <class Os> std::ostream& get();
struct stdout{};
struct stderr{};
template <> inline std::ostream& get<stdout>() { return std::cout; }
template <> inline std::ostream& get<stderr>() { return std::cerr; }
}}
// define a specialization for os::get()
#define DEFINE_FILE(ofs_name,filename)\
namespace internal{namespace os{ \
struct ofs_name{ \
std::ofstream ofs; \
ofs_name(){ ofs.open(filename);} \
~ofs_name(){ ofs.close(); delete this; } \
}; \
template <> inline std::ostream& get<ofs_name>(){ return (new ofs_name())->ofs; } \
}} \
using internal::os::ofs_name;
Multiple overloads on the same name do similar things. Specializations do the exact same thing, but on different types. Overloads have the same name, but may be defined in different scopes. A template is declared in only one scope, and the location of a specialization declaration is insignificant (although it must be at the scope of the enclosing namespace).
For example, if you extend std::swap to support your type, you must do so by specialization, because the function is named std::swap, not simply swap, and the functions in <algorithm> would be quite right to specifically call it as ::std::swap( a, b );. Likewise for any name that might be aliased across namespaces: calling a function may get "harder" once you qualify the name.
The scoping issue is confused further by argument-dependent lookup. Often an overload may be found because it is defined in proximity to the type of one of its arguments. (For example, as a static member function.) This is completely different from how the template specialization would be found, which is by simply looking up the template name, and then looking up the explicit specialization once the template has been chosen as the target of the call.
The rules of ADL are the most confusing part of the standard, so I prefer explicit specialization on the priciple of avoiding reliance on it.