tags:

views:

200

answers:

6

Learning C++, came upon function templates. The chapter mentioned template specialization.

  1. template <> void foo<int>(int);

  2. 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.

+1  A: 

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.

Martin York
The question was why not to use overloading instead of specialization?
Kirill V. Lyadvinsky
Martin, I think he was asking, why provide the specialization rather than a non-template overload of MySwap().
Drew Hall
+10  A: 

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.

David Rodríguez - dribeas
Good point about the `f<int>` thing.
Johannes Schaub - litb
There are several more differences. Overloads don't play well with qualified names, unless declared all in the same scope. Extending standard library functions requires specialization, not overloading. ADL's fickleness tends to increase with more overloads.
Potatoswatter
Though if your interface states that there is a function(-template) `f` that you can call, then you are not allowed to rely on something like `f<T>` to work and you don't need it either because the template argument is deduced anyway. Extending standard library functionality can be nicely done by ADL (like with `std::swap`), while it is often not possible to do by specializing it in namespace `std` (you cannot specialize it for a templated argument for example - this would require partial specialization). The range-based for loop is entirely based on ADL.
Johannes Schaub - litb
@Johannes: The library intro specifically requires that extension be done by specialization. See my answer as to why a `swap` overload won't work portably. Partial specialization is a problem, but it's going away in C++0x.
Potatoswatter
@Potatoswatter - I am sorry, but did not understand your point on 'Overloads don't play well with qualified names'.
Chubsdad
@Chubsdad: If the function is specified as being in one scope, but the overload is in another scope, then a call to the function's qualified name will miss the overload.
Potatoswatter
@Potatoswatter: Oh Ok. Aren't overloads always in the same scope except in case of name lookup involving ADL?
Chubsdad
@Chubsdad: Exactly.
Potatoswatter
@Chubsdad, @Potatoswatter My opinion is that specifying an explicit scope is the root of all evil. Hinders generic programming :)
Johannes Schaub - litb
+4  A: 

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.

Johannes Schaub - litb
Since function template speciailizations don't participate in overloading process, is `function template specialization' a benefit because it speeds up overload resolution
Chubsdad
@Chubsdad: I would never ever try to optimize compile time. Decisions should be made on higher grounds than the time it will take the compiler to process the code.
David Rodríguez - dribeas
You should rather try and optimize the compiler itself :)
Johannes Schaub - litb
A: 

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.

Jesper Madsen
A virtual method cannot be a template.
Potatoswatter
@Potato: I think he was drawing a parallel. If you declare a function virtual, but no derived classes overload it, the virtual kind of goes to waste. Its not a very good parallel, to be sure.
Dennis Zickefoose
A: 

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;   
Daniel
That doesn't look right. `stdout` is an object (and it is allowed to be a macro, which pretty much ruins everything) so it shouldn't match the argument type `class Os`. If anything, you need `template< FILE * > std::ostream`
Potatoswatter
@Potatoswatter: its internal (namespace internal). Later on it is brought into a public (not global) namespace of the library under a proper name. template<FILE*> is good also.
Daniel
A: 

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.

Potatoswatter
Well, formally overloading really happens only in one scope. If two function declarations are in different scopes, they don't overload each oher. But the actual point I want to make is - imagine you have a `template<typename T> class Container { ... };` - how do you want to specialize `std::swap` to work on that container? It's not possible, and you are likely ending up to do that using ADL. Because of that reason, generic functions will not just call `std::swap` because that would be a totally lame quality of implementation.
Johannes Schaub - litb
I think the general pattern is by writing `{ using std::swap; swap(a, b); }` which finds both declarations by ADL and specializations of `std::swap`. I find it easier to overload. Everyone should do it as he sees fit :)
Johannes Schaub - litb
@Johannes: You have a workable solution, but it's not what the standard specifies. The point is to implement `swap` for a class such that it's found by `<algorithm>`. (Yes, only the "best" scope is used to find a match… but than also be a source of frustration! There is a clear priority inversion there, as the user will probably not see scoping particulars as the most significant factor.)
Potatoswatter
@Johannes: As I mentioned under David's answer, the Standard simply doesn't provide for specializing `swap` on a class template. But C++0x will add function template partial specialization. Doesn't Bjarne even mention this in his FAQ as a reason that feature is important?
Potatoswatter
@Potatoswatter C++0x does not add function template partial specialization as far as I'm aware. What it adds are default template arguments for function templates.
Johannes Schaub - litb
@Johannes: You are correct. Huh, too much wishful thinking on my part :v( . Well, still for my own functions I think I'd prefer a workaround via a dummy class or a traits pattern instead of specialization-by-overloading. At the least, such overloads should be explicitly `enable_if`'d.
Potatoswatter