4

I would like to overload std::swap in my template class. In the following code (simplified)

#ifndef Point2D_H
#define Point2D_H

template <class T>
class Point2D
{
    protected:

            T x;
            T y;

    public:
            Point2D () :  x ( 0 ),  y ( 0 ) {}
            Point2D( const T &x_, const T &y_ ) :  x ( x_ ), y ( y_ ) {}
            ....

    public:

            void swap ( Point2D <T> &p );   
};


template <class T>
inline void swap ( Point2D <T> &p1, Point2D <T> &p2 ) { p1.swap ( p2 ); }


namespace std
{
    template <class T>
    inline void swap ( Point2D <T> &p1, Point2D <T> &p2 ) { p1.swap ( p2 ); }
}

template <class T>
void Point2D <T>::swap ( Point2D <T> &p ) 
{
    using (std::swap);
    swap ( x, p.x );
    swap ( y, p.y );
}

#endif

there is a compiler error (only in VS 2010):

error C2668: 'std::swap' : ambiguous call to overloaded 

I do not know why, std::swap should be overoaded... Using g ++ code works perfectly. Without templates (i.e. Point2D is not a template class) this code also works..

Thanks for your help.

Ian
  • 541
  • 2
  • 6
  • 8
  • 2
    You aren't supposed to **overload** `std::swap`, you are supposed to **specialize** it. Answers to the question linked by villintehaspam explain that distinction along with giving examples. But **partial specialization** isn't possible for function templates, so you'll have to define your implementation in your own namespace and rely on Koenig lookup. – Ben Voigt Jan 02 '11 at 21:20
  • Note: the standard says "An explicit specialization declaration shall not be a friend declaration." It was a nice idea though. – Ben Voigt Jan 02 '11 at 21:55

3 Answers3

5

Please see How to overload std::swap() . Basically, you are allowed to make a specialization of std::swap, but not an overload.

So, it is ok to create a specific version for a specific Point<> type (say Point<float>), but not for any Point<T>.

Community
  • 1
  • 1
villintehaspam
  • 8,540
  • 6
  • 45
  • 76
  • 1
    No but... you can put an overload of swap in the namespace where `Point` is defined, and it will be found via Koenig lookup (aka argument-dependent lookup). – Ben Voigt Jan 02 '11 at 21:21
  • @Ben Voigt: True for simple cases like when you call swap(a,b) in your own code - however there is plenty of code that calls std::swap explicitly (I think this is the case in at least some implementations of the standard library) and that won't help then. – villintehaspam Jan 02 '11 at 21:31
  • @villintehaspam It is a little bit uncomfortable to specialize swap for more data types (double, floast, ...): inline void swap ( Point2D &p1, Point2D &p2 ) { p1.swap ( p2 ); } – Ian Jan 02 '11 at 21:37
  • @Ian, Yes. Normally the need to specialize std::swap arises because you can make a performance improvement over naive copying. For simple cases like double, int etc. I don't think you have the need to specialize it for a class like Point2D in your example. So just don't do it - make sure that you add a copy constructor and assignment operator instead. – villintehaspam Jan 02 '11 at 21:44
  • @villintehaspam You are right, thanks for your comment. I would like to speed up the sorting of points, so I need to specialize std::swap for Point2D in container storing 2D Points... – Ian Jan 02 '11 at 21:49
  • @Ian: I think you may have misunderstood me. In your case, for the Point2D class, there is no performance gain for specializing std::swap. You only need to do it for more complex classes like if you were to implement your own std::vector or similar, in which case invoking the copy constructor would waste performance. – villintehaspam Jan 03 '11 at 02:05
2

I know you haven't asked this, but since you're using VC10, providing a move constructor and a move assignment operator should make std::swap() perform optimal for your type.

sbi
  • 219,715
  • 46
  • 258
  • 445
0

The reason it's ambiguous is because you provided ::swap and std::swap, then you using'd std::swap. Now you have both ::swap and std::swap in the global namespace. So the compiler doesn't know if you mean ::swap or std::swap.

However, providing move operator/constructor will make swap perform optimally, realistically.

Puppy
  • 144,682
  • 38
  • 256
  • 465