program tip

std :: swap ()을 오버로드하는 방법

radiobox 2020. 7. 28. 08:22
반응형

std :: swap ()을 오버로드하는 방법


std::swap()정렬 및 할당 중에 많은 std 컨테이너 (예 : std::liststd::vector)에서 사용됩니다.

그러나 표준 구현은 swap()사용자 정의 유형에 대해 매우 일반화되고 다소 비효율적입니다.

따라서 std::swap()사용자 정의 유형별 구현 으로 오버로드 하여 효율성을 얻을 수 있습니다 . 그러나 std 컨테이너에서 사용되도록 어떻게 구현할 수 있습니까?


스왑을 오버로드하는 올바른 방법은 스왑하는 것과 동일한 네임 스페이스에 스왑을 작성하여 인수 종속 조회 (ADL) 를 통해 찾을 수 있도록하는 것 입니다. 특히 쉬운 방법은 다음과 같습니다.

class X
{
    // ...
    friend void swap(X& a, X& b)
    {
        using std::swap; // bring in swap for built-in types

        swap(a.base1, b.base1);
        swap(a.base2, b.base2);
        // ...
        swap(a.member1, b.member1);
        swap(a.member2, b.member2);
        // ...
    }
};

주의 Mozza314

다음은 일반 std::algorithm호출 의 효과에 대한 시뮬레이션이며 std::swap사용자가 네임 스페이스 std에서 스왑을 제공하도록합니다. 실험이므로이 시뮬레이션은 namespace exp대신 사용 합니다 namespace std.

// simulate <algorithm>

#include <cstdio>

namespace exp
{

    template <class T>
    void
    swap(T& x, T& y)
    {
        printf("generic exp::swap\n");
        T tmp = x;
        x = y;
        y = tmp;
    }

    template <class T>
    void algorithm(T* begin, T* end)
    {
        if (end-begin >= 2)
            exp::swap(begin[0], begin[1]);
    }

}

// simulate user code which includes <algorithm>

struct A
{
};

namespace exp
{
    void swap(A&, A&)
    {
        printf("exp::swap(A, A)\n");
    }

}

// exercise simulation

int main()
{
    A a[2];
    exp::algorithm(a, a+2);
}

나를 위해 이것은 인쇄됩니다.

generic exp::swap

컴파일러가 다른 것을 인쇄하면 템플릿에 대해 "2 단계 조회"를 올바르게 구현하지 않은 것입니다.

컴파일러가 C ++ 98/03/11 중 하나를 준수하는 경우 표시되는 것과 동일한 출력을 제공합니다. 그리고 그 경우에 당신이 겪을 것 같은 것이 정확히 일어날 것입니다. 그리고 swap네임 스페이스 std( exp)에 넣는 것이 멈추지 않았습니다.

Dave and I are both committee members and have been working this area of the standard for a decade (and not always in agreement with each other). But this issue has been settled for a long time, and we both agree on how it has been settled. Disregard Dave's expert opinion/answer in this area at your own peril.

This issue came to light after C++98 was published. Starting about 2001 Dave and I began to work this area. And this is the modern solution:

// simulate <algorithm>

#include <cstdio>

namespace exp
{

    template <class T>
    void
    swap(T& x, T& y)
    {
        printf("generic exp::swap\n");
        T tmp = x;
        x = y;
        y = tmp;
    }

    template <class T>
    void algorithm(T* begin, T* end)
    {
        if (end-begin >= 2)
            swap(begin[0], begin[1]);
    }

}

// simulate user code which includes <algorithm>

struct A
{
};

void swap(A&, A&)
{
    printf("swap(A, A)\n");
}

// exercise simulation

int main()
{
    A a[2];
    exp::algorithm(a, a+2);
}

Output is:

swap(A, A)

Update

An observation has been made that:

namespace exp
{    
    template <>
    void swap(A&, A&)
    {
        printf("exp::swap(A, A)\n");
    }

}

works! So why not use that?

Consider the case that your A is a class template:

// simulate user code which includes <algorithm>

template <class T>
struct A
{
};

namespace exp
{

    template <class T>
    void swap(A<T>&, A<T>&)
    {
        printf("exp::swap(A, A)\n");
    }

}

// exercise simulation

int main()
{
    A<int> a[2];
    exp::algorithm(a, a+2);
}

Now it doesn't work again. :-(

So you could put swap in namespace std and have it work. But you'll need to remember to put swap in A's namespace for the case when you have a template: A<T>. And since both cases will work if you put swap in A's namespace, it is just easier to remember (and to teach others) to just do it that one way.


You're not allowed (by the C++ standard) to overload std::swap, however you are specifically allowed to add template specializations for your own types to the std namespace. E.g.

namespace std
{
    template<>
    void swap(my_type& lhs, my_type& rhs)
    {
       // ... blah
    }
}

then the usages in the std containers (and anywhere else) will pick your specialization instead of the general one.

Also note that providing a base class implementation of swap isn't good enough for your derived types. E.g. if you have

class Base
{
    // ... stuff ...
}
class Derived : public Base
{
    // ... stuff ...
}

namespace std
{
    template<>
    void swap(Base& lha, Base& rhs)
    {
       // ...
    }
}

this will work for Base classes, but if you try to swap two Derived objects it will use the generic version from std because the templated swap is an exact match (and it avoids the problem of only swapping the 'base' parts of your derived objects).

NOTE: I've updated this to remove the wrong bits from my last answer. D'oh! (thanks puetzk and j_random_hacker for pointing it out)


While it's correct that one shouldn't generally add stuff to the std:: namespace, adding template specializations for user-defined types is specifically allowed. Overloading the functions is not. This is a subtle difference :-)

17.4.3.1/1 It is undefined for a C++ program to add declarations or definitions to namespace std or namespaces with namespace std unless otherwise specified. A program may add template specializations for any standard library template to namespace std. Such a specialization (complete or partial) of a standard library results in undefined behaviour unless the declaration depends on a user-defined name of external linkage and unless the template specialization meets the standard library requirements for the original template.

A specialization of std::swap would look like:

namespace std
{
    template<>
    void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}

Without the template<> bit it would be an overload, which is undefined, rather than a specialization, which is permitted. @Wilka's suggest approach of changing the default namespace may work with user code (due to Koenig lookup preferring the namespace-less version) but it's not guaranteed to, and in fact isn't really supposed to (the STL implementation ought to use the fully-qualified std::swap).

There is a thread on comp.lang.c++.moderated with a long dicussion of the topic. Most of it is about partial specialization, though (which there's currently no good way to do).

참고URL : https://stackoverflow.com/questions/11562/how-to-overload-stdswap

반응형