일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- resource management class
- 티스토리챌린지
- 비교 함수 객체
- 상속
- 영화 리뷰
- Smart Pointer
- UE4
- effective stl
- c++
- Vector
- Effective c++
- more effective c++
- lua
- operator new
- 스마트 포인터
- 참조자
- 오블완
- 암시적 변환
- 영화
- 다형성
- reference
- exception
- 언리얼
- virtual function
- 루아
- implicit conversion
- 반복자
- 게임
- 메타테이블
- 예외
Archives
- Today
- Total
스토리텔링 개발자
[Effective C++] 45. 멤버 함수 템플릿으로 암시적 변환 지원 본문
728x90
항목 45. “호환되는 모든 타입”을 받아들이는데는 멤버 함수 템플릿이 직방!
포인터의 암시적 변환(implicit conversion)
- 스마트 포인터로는 대신할 수 없는 포인터의 특징이다.
// 포인터의 경우
class Top { ... };
class Middle : public Top { ... };
class Bottom : public Middle { ... };
Top* pt1 = new Middle; // Middle* -> Top* 암시적 변환
Top* pt2 = new Bottom; // Bottom* -> Top* 암시적 변환
const Top* pct2 = pt1; // Top* -> const Top* 암시적 변환
// 사용자 정의 스마트 포인터의 경우
template<typename T>
class SmartPtr
{
public:
explicit SmartPtr(T* realPtr);
...
};
SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle); // 실패!
SmartPtr<Top> pt2 = SmartPtr<Bottom>(new Bottom); // 실패!
SmartPtr<const Top> pct2 = pt1; // 실패!
- 같은 템플릿으로 만들어진 다른 인스턴스들 사이에는 어떤 관계도 없으므로 컴파일러 입장에서는 SmartPtr<Top>과 SmartPtr<Middle>은 완전히 별개의 클래스이다.
- 결국, 이 변환을 가능하게 하려면 변환 방법을 직접 마련해야 한다.
스마트 포인터 간의 변환을 지원하는 방법
- 생성자 함수를 직접 만든다.
- SmartPtr<Middle> 혹은 SmartPtr<Bottom>으로부터 SmartPtr<Top>을 생성할 수는 있겠지만..
- 클래스 계통이 확장될때마다 유지보수를 해주어야 하고, 그때마다 SmartPtr을 수정하고 싶지는 않다.
- 생성자를 만들어내는 템플릿(template)을 사용한다.
- 멤버 함수 템플릿(member function template, 멤버 템플릿)의 한 예시라고 할 수 있다.
멤버 함수 템플릿(member function template)
- 어떤 클래스의 멤버 함수를 찍어내는 템플릿
template<typename T>
class SmartPtr
{
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other); // 일반화된 복사 생성자를 위한 멤버 템플릿
...
}
- 위 예시는 일반화 복사 생성자(generalized copy constructor)라고 할 수 있다.
- 같은 템플릿을 써서 인스턴스화 되지만 타입이 다른 타입의 객체로부터 원하는 객체를 만들어주는 생성자
일반화 복사 생성자 살펴보기
- explicit으로 선언되지 않았다.
- 기본제공 포인터의 동작과 같이 타입 변환이 암시적으로 이루어지며, 캐스팅이 필요하지 않게 하기 위해서 제거하였다.
- 기본제공 포인터보다 더 많은 일을 해준다.
- SmartPtr<Bottom>으로부터 SmartPtr<Top>을 만드는 것만을 원했으나..
- SmartPtr<Top>으로부터 SmartPtr<Bottom>을 만드는 것까지 지원하는 형태이다.
- 심지어 SmartPtr<double>으로부터 SmartPtr<int>를 만든다거나 하는 것까지 가능하다.
- 기본제공 포인터에서는 int*에서 double* 암시적 변환은 불가능한데...
기본제공 포인터만큼만 일하도록 수정해보자
- 기본 제공 포인터의 getter를 사용하여 암시적 변환이 가능할때만 컴파일 되도록 한다.
template<typename T>
class SmartPtr
{
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other) : heldPtr(other.get()) // U*로 T*를 초기화
{ ... }
T* get() const { return heldPtr; } // getter 함수 제공
...
private
T* heldPtr; // 기본 제공 포인터
}
- 이렇게 하면, U*에서 T*로 진행되는 암시적 변환이 가능할 때만 컴파일이 된다!
멤버 함수 템플릿의 추가적인 활용법
- 대입 연산에 사용될 수 있다.
- 아래 예시에서 생성자 템플릿과 대입 연산자 템플릿을 함께 쓴 것을 확인할 수 있다.
std::shared_ptr 클래스 템플릿
-
template<class T> shared_ptr { public: // 생성자 template<class Y> explicit shared_ptr(Y* p); template<class Y> shared_ptr(shared_ptr<Y> const& r); template<class Y> explicit shared_ptr(weak_ptr<Y> const& r); template<class Y> explicit shared_ptr(auto_ptr<Y>& r); // 대입 연산자 template<class Y> shared_ptr& operator=(shared_ptr<Y> const& r); template<class Y> shared_ptr& operator=(auto_ptr<Y>& r); ...
- 호환되는 모든 기본제공 포인터, std::shared_ptr, auto_ptr, weak_ptr 객체들로부터 생성자 호출이 가능하다.
- weak_ptr을 제외한 나머지를 모두 대입 연산에 사용할 수 있다.
- 생성자 중 일반화 복사 생성자(shared_ptr<Y> const&에 대한 복사 생성자)만 explicit이 빠져 있는 이유.
- shared_ptr로 만든 어떤 타입에서 또 다른 shared_ptr 타입으로의 암시적 변환은 허용한다.
- 하지만 기본제공 포인터나 다른 스마트 포인터 타입으로부터의 암시적 변환은 막겠다.
- 매개변수로 auto_ptr만 const로 선언되지 않은 이유.
- auto_ptr은 복사 연산으로 인해 객체가 수정될 때 복사된 쪽 하나만 유효하게 남는 특성이 있다.
- 그러므로 매개변수로 넘겨진 기존 auto_ptr은 유효하지 않게 수정되어야 하므로 const로 선언하면 안 된다.
유의할 점
- 일반화 복사 생성자가 선언되어 있더라도, 복사 생성자와 복사 대입 생성자를 컴파일러가 알아서 만드는 경우가 있다.
- 멤버 함수 템플릿이 C++ 언어의 기본 규칙까지 바꾸진 않기 때문이다.
- .C++ 언어의 기본 규칙
- 복사 생성자가 필요한데 프로그래머가 직접 선언하지 않으면 컴파일러가 자동으로 하나 만든다.
- 하지만 일반화 복사 생성자는 컴파일러가 복사 생성자를 만드는 것을 가로막는 요소가 아니다!
- 따라서 복사 생성자가 자동 생성되는 걸 막으려면 보통 복사 생성자도 직접 선언해 줘야 한다.
template<class T> class shared_ptr
{
public:
shared_ptr(shared_ptr const& r); // 복사 생성자
template<class Y>
shared_ptr(shared_ptr<Y> const& r); // 일반화 복사 생성자
sahred_ptr& operator=(shared_ptr const& r); // 복사 대입 연산자
template<class Y>
shared_ptr& operator=(shared_ptr<Y> const& r); // 일반화 복사 대입 연산자
...
}
728x90
'개발 > Effective C++' 카테고리의 다른 글
[Effective C++] 47. 특성 정보 클래스 (0) | 2024.07.19 |
---|---|
[Effective C++] 46. 템플릿 클래스 안에 비멤버 함수 두기 (0) | 2024.07.18 |
[Effective C++] 44. 템플릿 코드 비대화 회피하기 (0) | 2024.07.16 |
[Effective C++] 43. 템플릿 부모 클래스의 인터페이스에 접근하기 (0) | 2024.07.15 |
[Effective C++] 42. typename의 두 가지 용법 (0) | 2024.07.12 |
Comments