std :: swap ()을 오버로드하는 방법
std::swap()
정렬 및 할당 중에 많은 std 컨테이너 (예 : std::list
및 std::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
'program tip' 카테고리의 다른 글
자바 스크립트에서 기기 너비를 가져옵니다. (0) | 2020.07.28 |
---|---|
업로드하기 전에 이미지 미리보기 표시 (0) | 2020.07.28 |
C ++에서 stringstream에서 string으로 어떻게 변환합니까? (0) | 2020.07.28 |
C #에서 마우스 클릭을 어떻게 시뮬레이트합니까? (0) | 2020.07.28 |
파이썬은 여러 파일 형식을 가져옵니다 (0) | 2020.07.28 |