Search code examples
c++visual-studio-2010visual-c++argument-dependent-lookupcopy-and-swap

Providing swap() for a C++ template class breaks std::swap()?


I was trying to implement the copy-and-swap idiom in my custom Matrix class, and I ran into some trouble with the implementation of swap() in the way suggested in the linked-to question:

(The compiler I used is the one from MS VS2010 IDE, dialect is good old-fashioned C++03.)

// matrix.h

namespace my_space 
{

template<typename T> class Matrix
{
public:
    /* ... */

    friend void swap(Matrix<T> &first, Matrix<T> &second)
    {
        using std::swap;
        swap(first.width_, second.width_);
        swap(first.height_, second.height_);
        swap(first.data_, second.data_);
    }
};

} // namespace

Now I have trouble reaching regular std::swap() in the code for functions residing in this namespace:

// some_code.cpp:
#include "matrix.h"
#include <algorithm>

using namespace my_space;
using namespace std;

// SomeClass is part of my_space!
void SomeClass::some_function()
{
    int a = 3, b = 7;
    swap(a,b);  // I wan't std::swap!
}

Unfortunately, for some reason, my_space::swap() for Matrix seems to alias all other calls to std::swap(), and I've no idea why since the arguments don't fit and ADL should favor std::swap:

1>f:\src\some_code.cpp(653): error C3767: 'swap': candidate function(s) not accessible
1>          could be the friend function at 'f:\src\matrix.h(63)' : 'swap'  [may be found via argument-dependent lookup]

(The error repeats 10 times for every line where I'm trying to use std::swap)

Does my_space::swap() always overrule std::swap() in my_space, even if the arguments don't fit? It's not as if std::swap() is not visible, and it worked OK before my_space::swap() was created.


Solution

  • The approach taken by STL containers uses a member function and then overload the static function. For example:

    template<class T, class Alloc=std::allocator<T> >
    class vector
    {
       T *data;
       size_t n;
       size_t max_n;
    public:
       void swap(vector<T, Alloc> &other)
       {
          swap(this->data, other.data);
          swap(this->n, other.n);
          swap(this->max_n, other.max_n);
       }
    };
    
    template<class T, class A>
    void swap(vector<T, A> &lhs, vector<T, A> &rhs)
    {
       lhs.swap(rhs);
    }
    

    In the suggested Matrix class, simply take the same approach...

    namespace my_space
    {
    template<typename T>
    class Matrix
    {
       unsigned width_;
       unsigned height_;
       std::vector<T> data_;
    public:
       void swap(Matrix<T> &other)
       {
          std::swap(this->width_, other.width_);
          std::swap(this->height_, other.height_);
          std::swap(this->data_, other.data_);  // calls this->data_.swap(other.data_);
       }
    };
    }
    
    namespace std
    {
       template<typename T>
       void swap(my_space::Matrix<T> &lhs, my_space::Matrix<T> &rhs)
       {
          lhs.swap(rhs);
       }
    }