이것만은 잊지 말자!
- std::swap이 여러분의 타입에 대해 느리게 동작할 여지가 있다면 swap 멤버 함수를 제공
- 이 멤버 swap은 예외를 던지지 않도록 만들자
- 멤버 swap을 제공한다면 멤버를 호출하는 비멤버 swap도 제공하자.
- 템플릿이 아닌 클래스에 대해서는 std::swap도 특수화하자
- 사용자 입장에서 swap을 호출할 때 std::swap에 대한 using 선언을 넣어 준 후에 네임스페이스 한정 없이 swap을 호출
- 사용자 정의 타입에 대한 std 템플릿을 완전 특수화하는 것은 가능
- std에 어떤 것이라도 새로 추가하지 말자
// 표준 swap
// 복사만 제대로 지원하는 타입이면 가능
// 복사 3번이 일어남
namespace std {
template<typename T>
void swap(T& a, T& b)
{
T temp(a);
a = b;
b = temp;
}
}
// pImpl 관용구와 비슷하다면 표준 swap 효율이 안좋음
// 특수화 템플릿을 사용하지 않으면 Widget과 WidgetImp1 포인터를 복사
// private 멤버에 접근하기 위해 특수화 함수를 프렌드로 선언하면 표준 템플릿 규칙에 어긋남
// 기존 STL 컨테이너와 일관성 유지
class WidgetImp1
{
priavte:
int a, b, c;
};
class Widget
{
public:
void swap(Widget& other)
{
swap(pImp1, other.pImp1);
}
private:
WidgetImp1* pImp1;
};
namespace std
{
template<>
void swap<Widget>(Widget& a, Widget& b)
{
a.swap(b);
}
}
// 클래스 템플릿에 대한 Swap
template<typename T>
class WidgetImp1 { ... };
template<typename T>
class Wdiget { ... };
// c++은 부분 특수화를 클래스 템플릿에 허용, 함수 템플릿에 허용 x => 컴파일 x
namespace std
{
template<typename T>
void swap<Widget<T>>(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
}
}
// 함수 템플릿을 부분 특수화 하고 싶을 때는 오버로드를 이용
// std의 템플릿에 대한 완전 특수화는 가능하지만 새로운 템플릿을 추가하는 것을 불가능
// 컴파일은 되지만 결과가 미정의 동작
// 따라서 std가 아닌 namespace 사용
// 컴파일러는 이름 탐색 규칙에 의해 Widget 네임스페이스 버전을 찾음
namespace Widget
{
template<typename T>
void swap(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
}
}
// 사용자 측면
template<typename T>
void doSomething(T& obj1, T& obj2)
{
using std::swap; // std::swap를 이 함수 안으로 끌어옴
swap(obj1, obj2); // 이름 탐색 규칙에 의해 T 전용 버전을 못 찾으면 std::swap을 사용
// std의 swap을 사용, std가 아닌 네임스페이스 T 전용 swap 사용 불가
// 완전 특수화가 중요한 이유가 이렇게 한정자를 붙여도 특수화 템플릿 사용 가능
std::swap(obj1, obj2);
}
클래스 타입 전용의 swap이 많은 곳에서 호출하고 싶다면 네임스페이스 안의 비멤버 함수 사용
멤버 버전의 swap은 절대로 예외를 던지지 말자
- 클래스 및 클래스 템플릿이 강력한 예외 안전성 보장을 제공하도록 하는 방법이 따로 있음(항목 29)
- 비멤버의 경우 표준 swap에 사용되는 복사 생성 및 복사 대입 함수는 예외 발생 허용이 되므로 예외
이름 탐색 규칙(ADL, Argument Dependent Lookup, 인자 기반 탐색, 쾨니그 탐색)
- 어떤 함수에 어떤 타입의 인자가 있으면 그 함수의 이름을 찾기 위해 해당 타입의 인자가 위치한 네임스페이스 내부의이름을 탐색해 들어간다는 간단한 규칙
'C++ > Effective C++' 카테고리의 다른 글
Effective C++ 항목 27 캐스팅은 절약, 또 절약! 잊지 말자 (0) | 2024.07.25 |
---|---|
Effective C++ 항목 26 변수 정의는 늦출 수 있는 데까지 늦추는 근성을 발휘하자 (1) | 2024.07.24 |
Effective C++ 항목 24 타입 변환이 모든 매개변수에 대해 적용되어야 한다면 비멤버 함수를 선언하자 (0) | 2024.07.22 |
Effective C++ 항목 23 멤버 함수보다는 비멤버 비프렌드 함수와 더 가까워지자 (0) | 2024.07.19 |
Effective C++ 항목 22 데이터 멤버가 선언될 곳은 private 영역임을 명심하자 (0) | 2024.07.17 |