std::swap()은 정렬 및 심지어 할당 중에 많은 std 컨테이너(예:
std::list및
std::vector`)에서 사용됩니다.
그러나 swap()
의 std 구현은 매우 일반화되어 있으며 사용자 정의 유형에는 다소 비효율적입니다.
따라서 사용자 정의 유형별 구현으로 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);
// ...
}
};
다음은 '', '일반' 표준용량 알고리즘입니다 호출하십시오 표준용량 스왑이란 미칠 배빠르게 한 사용자가 있는 그들의 스왑하는 구매와 제공하십시오 이름공간이 표준용량. 이를 이용한 실험을 통해 ',' 대신 '이름공간이 표준용량' 이 배빠르게 /dev/raw/raw1 이름공간이 exp.
// 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
그 후 제대로 구현하는 것은 뭔가 다른 게 있다면, 이 상 를출력합니다 컴파일러와의 " lookup". 템플릿.
(모든 c++ 컴파일러는 따를 경우 98/03/11), 그런 다음 동일한 출력을 부여하느뇨 가르켜. 정확히 어떤 두려움이, 이런 경우에는 일어나지 않는 것입니다. 네, 전면에 내세우는 '스왑이란' 에 '표준용량' ('확장') 이 발생하지 않도록 이름공간이 멈추지 않았다.
정보위 소속 의원들은 모두 donatelli 쉐퍼드도 십 년 동안 이 지역 및 행한 기준이 아닌 합의에 따라 항상 서로). 그러나 이 문제는 그동안 오랫동안 정착하고 있는 데, 우리가 어떻게 현재까지 모두 마무리됐다. # 39 이 분야의 전문가 의견조사를 통해 현재 자신의 Dave& disregard, s / 답변을 한다.
이 문제는 큐피드라는 표시등으로 C++98 후에 출판되었다. 작동합니까 이 지역 donatelli 쉐퍼드도 시작 약 2001년 시작했다. 이것이 현대 해결책:
// 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);
}
이제 다시 doesn& # 39, 빗나갔다. :- (
그러니까 '에서' 스왑하는 이름공간이 표준용량 및 할꺼이나 작동합니까 있다고 한다. 그러나 you& # 39, & # 39 스왑이란 '에서' ll 기억할 필요가 있어, 'A' s 이름공간이 경우에 대비하여 템플리트를: t> A<, ','. 두 경우 모두 사용할 수 있는 '뒤', 'A' s & # 39 스왑할 놓으면 바로 네임스페이스인 쉽게 기억할 수 있는 방법 중 하나는 (그리고 다른 teacher) 를 just do it.
(C++ 표준에 따라) std::swap을 오버로드하는 것은 허용되지 않지만, 자체 유형에 대한 템플릿 전문화를 std 네임스페이스에 추가하는 것은 특별히 허용됩니다. 예
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}
를 추가하면 std 컨테이너(및 다른 곳)에서 사용할 때 일반적인 특수화 대신 해당 특수화가 선택됩니다.
또한 스왑의 기본 클래스 구현을 제공하는 것만으로는 파생 유형에 충분하지 않다는 점에 유의하세요. 예를 들어 다음과 같은 경우
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}
가 있다면 베이스 클래스에서는 작동하지만, 두 개의 파생 객체를 스왑하려고 하면 템플릿 스왑이 정확히 일치하기 때문에 (그리고 파생 객체의 &39;base' 부분만 스왑하는 문제를 피할 수 있기 때문에) std의 제네릭 버전을 사용합니다.
참고: 지난 답변에서 잘못된 부분을 제거하기 위해 이 부분을 업데이트했습니다. (지적해 주신 puetzk와 j_random_hacker에게 감사드립니다.)
일반적으로 std:: 네임스페이스에 항목을 추가해서는 안 되는 것이 맞지만, 사용자 정의 유형에 대한 템플릿 전문화를 추가하는 것은 특별히 허용됩니다. 함수를 오버로드하는 것은 허용되지 않습니다. 이것은 미묘한 차이입니다 :-)
17.4.3.1/1 C++ 프로그램에서 선언이나 정의를 추가하는 것은 정의되지 않습니다. 선언이나 정의를 추가하는 것은 정의되지 않았습니다. 지정하지 않는 한. 프로그램은 모든 표준 라이브러리 템플릿에 대한 템플릿 특수화를 표준 라이브러리 템플릿에 대한 템플릿 특수화를 추가할 수 있습니다. 이러한 특수화는 (전체 또는 부분) 표준 라이브러리의 이러한 특수화는 정의되지 않은 선언이 사용자 정의 이름에 의존하지 않는 한 정의되지 않은 동작을 초래합니다. 외부 링크에 의존하지 않는 한, 그리고 템플릿 전문화가 원본 템플릿의 표준 라이브러리 요구 사항을 표준 라이브러리 요구 사항을 충족하지 않는 한 정의되지 않은 동작이 발생합니다.
std::swap
namespace std
{
template<>
void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}
템플릿이 없으면 허용되는 특수화가 아니라 정의되지 않은 과부하가 될 것입니다. Wilka가 제안한 기본 네임스페이스를 변경하는 접근 방식은 사용자 코드에서 작동할 수 있지만(Koenig 조회는 네임스페이스가 없는 버전을 선호하기 때문에) 보장되지 않으며 실제로 그렇게 할 필요도 없습니다(STL 구현은 정규화된 std::swap을 사용해야 합니다).
이 주제에 대한 긴 토론이 있는 comp.lang.c++.moderated의 스레드가 있습니다. 하지만 대부분은 부분적인 전문화에 관한 내용입니다(현재로서는 좋은 방법이 없습니다).