swapの特殊化

ユーザ定義型に対してswapを特殊化することは良くある.で,このやり方に2通り考えられる.

  • std::swapを特殊化する(#1)
  • その型と同じ名前空間でswapを定義する(#2)

それぞれ,以下のようになる.

namespace my_lib{
  class X{
    .....
  };

  void swap(X &x, X &y) // #2
  {
    .....
  }
}

#include <algorithm> // std::swapのprimary templateの宣言を取り込む

namespace std{
  void swap(my_lib::X &x, my_lib::X &y) // #1
  {
    .....
  }
}

上の場合は#1も#2も問題ない.ところが,ユーザ定義型がテンプレートの場合#1が不可能になる.

namespace my_lib{
  template<class T>
  class X
  {
    .....
  };

  template<class T>
  void swap(X<T> &x, X<T> &y) // #2
  {
    .....
  }
}

#include <algorithm>

namespace std{
  template<class T>
  void swap(my_lib::X<T> &x, my_lib::X<T> &y) // #1:エラー!関数テンプレートの部分特殊化は不可能!
  {
    .....
  }
}

一方でswapを使う関数の側,例えばstd::sortではどのようにswapを呼ぶのかを考えてみる.以下の2つが考えられる.

  • swap(x, y)
  • ::std::swap(x, y)
namespace std{
  template<class RandomAccessIterator>
  void sort(RandomAccessIterator first, RandomAccessIterator last)
  {
    RandomAccessIterator it1, it2;
    .....
    swap(*it1, *it2); // #2 -> #1 -> 汎用のstd::swap の順で採用される
    .....
  }
}
namespace std{
  template<class RandomAccessIterator>
  void sort(RandomAccessIterator first, RandomAccesssIterator last)
  {
    RandomAccessIterator it1, it2;
    .....
    ::std::swap(*it1, *it2); // #1 -> 汎用のstd::swapの順で採用される(#2はLook Upの対象から外れる)
    .....
  }
}

対象がテンプレートの場合#2が必要になることから考えて前者が採用されてしかるべきだろう.現在の規格ではこれは保証されていないが将来的には保証されるみたい.
ちなみにstd名前空間以外の場所で汎用関数からswapを呼ぶ場合には一工夫必要になる.

namespace my_lib{
  template<class T>
  void f()
  {
    T x, y;
    .....
    using std::swap;
    swap(x, y); // #2 -> #1 -> 汎用のstd::swapの順で採用される
    .....
  }
}

using std::swapが無いとTが組み込み型のとき困ることになる.