Effective C++ Item 252009年08月11日 02時26分34秒

  • スワップ関数は std::swap として提供されている。
  • もし、メンバ関数の swap を提供するのであれば、非メンバ関数の swap も準備する事。 さらにクラスの場合は std::swap も特殊化する。しかしクラステンプレートの場合は特殊化は出来ない。
  • swap 関数を呼ぶときには using std を関数内で使い std::swap も 名前空間を指定しないでも呼べるようにする。
  • 専用の swap 関数を作るのは構わないが、他の副作用を追加しないこと。

一般的なスワップ関数の実装として以下の例をあげている。


namespace std
{
    template<typename T>
    void swap(T& a, T&anp; b)
    {
        T temp(a);
        a = b;
        b = temp;
    }
}

この形のスワップ関数を効率良くするためには、クラスを実装の為のクラスと、実装を指すメンバ変数のみの本体の二つに分ける。


class WidgetImpl
{
    private:
        int a, b, c;
        std::vector<double> v;
}

class Widget
{
    private:
        WidgetImpl *pImpl;
}

これにより、ポインタ一つの入れ換えになるので、動作が軽くなる。

特殊化の技法を基にして、Widget クラスの swap を作ることが出来る。


class Widget
{
    public:
        void swap(Widget& other)
        {
            using std::swap;
            swap(pImpl, other.pImpl);
        }
}

namespace std
{
    template<>
    void swap<Widget>(Widget& a, Widget& b)
    {
        a.swap(b);
    }
}

メンバ関数のスワップ関数を作り、非メンバ関数から呼び出す。

std 名前空間は特殊で新しいテンプレート、クラス、関数などを入れる事は出来ない。その為、このクラスがクラステンプレートであった場合はこの様な特殊化は出来ない。そこで、swap 関数を std 名前空間に入れることはしないが、クラステンプレートと同じ名前空間内に非メンバ関数を用意する。 書式は以下の様になる。


namespace WidgetStuff
{
    template<typename T>
    class Widget
    {
        void swap(Widget<T>& other);
    };

    template<typename T>
    void swap(Widget<T>& a, Widget<T&t;& b)
    {
        a.swap(b);
    }
}

クラステンプレートでは、実行時に一番適した関数が呼ばれる様に手助けをする必要がある。

template<typename T>
void doSomething(T& obj1, T& obj2)
{
    using std::swap;
    swap(obj1, obj2);
}

C++ の機構により、obj1 と obj2 に適した型の関数が探される。もし、特殊化された swap 関数があれば、それが呼ばれる。 もし、特殊化 swap 関数が無い時には std から呼ばれるように using std::swap を入れる。std::swap の形で呼ぶと、特殊化 swap 関数が呼ばれなくなってしまう。

前回次回