문제

std::swap() 많은 표준 컨테이너(예: std::list 그리고 std::vector) 정렬 및 할당 중에도 마찬가지입니다.

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

따라서 과부하를 통해 효율성을 얻을 수 있습니다. std::swap() 사용자 정의 유형별 구현을 사용합니다.그러나 표준 컨테이너에서 사용되도록 어떻게 구현할 수 있습니까?

도움이 되었습니까?

해결책

스왑을 오버로드하는 올바른 방법은 스왑하는 것과 동일한 네임스페이스에 작성하여 다음을 통해 찾을 수 있도록 하는 것입니다. 인수 종속 조회(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와 나는 둘 다 위원회 회원이며 10년 동안 이 표준 영역에서 작업해 왔습니다(항상 서로 동의하지는 않았습니다).하지만 이 문제는 오랫동안 해결되어 왔으며, 우리 둘 다 문제가 어떻게 해결되었는지에 대해 동의합니다.이 분야에 대한 Dave의 전문가 의견/답변은 귀하의 책임하에 무시하십시오.

이 문제는 C++98이 출시된 후에 드러났습니다.2001년쯤부터 Dave와 나는 이 지역에서 일하세요.이것이 현대적인 솔루션입니다.

// 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);
}

출력은 다음과 같습니다

swap(A, A)

업데이트

다음과 같은 관찰이 이루어졌습니다.

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

}

공장!그렇다면 그것을 사용하는 것은 어떨까요?

당신의 경우를 고려하십시오 A 클래스 템플릿은 다음과 같습니다.

// 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);
}

이제는 다시 작동하지 않습니다.:-(

그래서 당신은 넣을 수 있습니다 swap 네임스페이스 std에 넣고 작동시키세요.하지만 다음을 입력하는 것을 기억해야 합니다. swap ~에 A템플릿이 있는 경우의 네임스페이스: A<T>.그리고 두 경우 모두 작동하므로 swap ~에 A의 네임스페이스를 사용하므로 한 가지 방식으로 수행하는 것이 기억하기(그리고 다른 사람에게 가르치기) 더 쉽습니다.

C++ 표준에 따라 std::swap을 오버로드하는 것은 허용되지 않습니다. 그러나 자신의 유형에 대한 템플릿 전문화를 std 네임스페이스에 추가하는 것은 특별히 허용됩니다.예:

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

그러면 표준 컨테이너(및 다른 곳)의 사용법이 일반 전문화 대신 전문화를 선택합니다.

또한 기본 클래스의 스왑 구현을 제공하는 것만으로는 파생 유형에 충분하지 않습니다.예:당신이 가지고 있다면

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

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

이는 기본 클래스에서 작동하지만 두 개의 파생 개체를 바꾸려고 하면 템플릿 스왑이 정확히 일치하기 때문에 std의 일반 버전을 사용하게 됩니다(그리고 파생 개체의 '기본' 부분만 바꾸는 문제를 피할 수 있습니다) ).

메모:마지막 답변에서 잘못된 부분을 제거하기 위해 이것을 업데이트했습니다.맙소사!(이를 지적해준 puetzk와 j_random_hacker에게 감사드립니다)

일반적으로 std::에 항목을 추가하면 안 되는 것이 맞지만네임스페이스에는 사용자 정의 유형에 대한 템플릿 특수화 추가가 특별히 허용됩니다.함수를 오버로드하는 것은 아닙니다.미묘한 차이입니다 :-)

17.4.3.1/1 C ++ 프로그램은 달리 지정되지 않는 한 네임 스페이스 STD 또는 네임 스페이스 네임 스페이스에 선언 또는 정의를 추가하기 위해 정의되지 않았습니다.프로그램은 모든 표준 라이브러리 템플릿에 대한 템플릿 전문화를 네임 스페이스 STD에 추가 할 수 있습니다.표준 라이브러리의 이러한 전문화 (완전 또는 부분)는 선언이 사용자 정의 외부 연결 이름에 의존하지 않는 한 정의되지 않은 동작을 초래하고 템플릿 전문화가 원래 템플릿의 표준 라이브러리 요구 사항을 충족하지 않는 한.

std::swap의 전문화는 다음과 같습니다:

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

template<> 비트가 없으면 허용되는 특수화가 아니라 정의되지 않은 오버로드가 됩니다.기본 네임스페이스를 변경하는 @Wilka의 제안 접근 방식은 사용자 코드에서 작동할 수 있지만(네임스페이스 없는 버전을 선호하는 Koenig 조회로 인해) 보장되지 않으며 실제로는 그렇지 않습니다(STL 구현은 완전히 -한정된 std::swap).

이있다 comp.lang.c++.moderated의 스레드 주제에 대한 토론.하지만 대부분은 부분 전문화에 관한 것입니다(현재는 이를 수행할 수 있는 좋은 방법이 없습니다).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top