tags:

views:

69

answers:

4

The usual definition for a specialization of a template function is something like this:

class Foo {
    [...]
};

namespace std {
    template<>
    void swap(Foo& left, Foo& right) {
        [...]
    }
} // namespace std

But how do you properly define the specialization when the type it's specialized on is itself a template? Here's what I've got:

template <size_t Bits>
class fixed {
    [...]
};

namespace std {
    template<size_t Bits>
    void swap(fixed<Bits>& left, fixed<Bits>& right) {
        [...]
    }
} // namespace std

Is this the right way to declare swap? It's supposed to be a specialization of the template function std::swap, but I can't tell whether the compiler is seeing it as such, or whether it thinks that it's an overload of it or something.

A: 

A template parameter cannot be declared with a value that is not determined until run-time. To use numbers for a template parameter, it needs to be a 'const' so it can be resolved at compile-time:

template <const size_t Bits> 
class fixed { 
    [...] 
}; 

namespace std { 
    template<const size_t Bits> 
    void swap(fixed<Bits>& left, fixed<Bits>& right) { 
        [...] 
    } 
} // namespace std 
Remy Lebeau - TeamB
Eh? A template parameter has to be a compile-time constant, but I've never had any problem declaring it without the `const`. Not even a warning.
Head Geek
+3  A: 

Partial specializations of function templates are not allowed, so the template for fixed<Bits> declares a new overload, not a specialization.

This doesn't matter, though, since a call like std::swap(a, b) will just choose the best matching function. It doesn't require that the function is a specialization of the generic std::swap template. If the best matching function is a overload instead of a specialization, the overload will be called.

sth
+4  A: 

Your solution isn't a template specialization, but an overload of a function in the std namespace, which is "undefined behavior" per the c++ standard.

This question is exactly your question.

Scott Meyers discusses this in Effective C++, and there is a followup thread on usenet's comp.lang.c++.

  • He suggests that you define it in fixed's own namespace.
  • Make sure 'fixed' is in a namespace.
  • Don't prefix calls to swap with 'std::'.
  • Let Koenig (a.k.a. argument dependent) lookup find the right swap function.

If you're seeing compilation errors when trying to define this in namespace std, it's likely due to your unfortunate choice of class names :) When within namespace std "fixed" is being seen as std::fixed, the floating point precision operator.

Stephen
The actual name is `fixed_integer`, I shortened it for the question. But if I declare it in a different namespace, won't all of the functions in the `std` namespace fail to call the right one? And don't most uses of `std::swap` specify the `std` namespace?
Head Geek
Added a wikipedia link which describes ADL for the swap use case.Koenig lookup says that calls to non-namespace-qualified swap(N::arg1, N::arg2) will use the namespace of the arguments to determine a better fit than std::swap. Most code I've seen doesn't explicitly call std::swap. Prefer the "using std::swap" approach.
Stephen
@Head Geek: std lib implementations _should_ use Koenig lookup for `std::swap()`, so they pick up user-defined overloads. I'm not sure they do this, though.
sbi
+2  A: 

Syntactically your "specialization" is done correctly. However, as others already noted, it is not really a partial specialization but a function overload. Function templates don't support partial specialization.

It is worth noting though that adding anything to namespace std is prohibited by the language specification (see 17.4.3.1/1). I understand why you insisted on doing it in std specifically, since you were trying to partially specialize std::swap. Since your swap is really an overload and not a specialization, you can safely do it outside std, within the same namespace that contains yor fixed. Argument-dependent name lookup ("Koenig lookup") should make sure that your swap is found by name lookup when applied to arguments of type fixed (unless the calling code explicitly refers to std::swap instead of just swap).

AndreyT