템플릿 선언(
class
,
function
, 그리고
variables
(C++14부터)
)은
멤버 명세
내부에 나타날 수 있으며, 이는
지역 클래스
가 아닌 모든 class, struct, union에 적용됩니다.
멤버 템플릿의 부분 특수화는 클래스 범위와 이를 둘러싼 네임스페이스 범위 모두에서 나타날 수 있습니다. 명시적 특수화는 기본 템플릿이 나타날 수 있는 모든 범위에서 나타날 수 있습니다.
struct A
{
template<class T> struct B; // 기본 멤버 템플릿
template<class T> struct B<T*> {}; // OK: 부분 특수화
// template<> struct B<int*> {}; // CWG 727을 통한 OK: 완전 특수화
};
template<> struct A::B<int*> {}; // OK
template<class T> struct A::B<T&> {}; // OK
바깥쪽 클래스 선언이 차례로 클래스 템플릿인 경우, 멤버 템플릿을 클래스 바디 외부에서 정의할 때 두 세트의 템플릿 매개변수를 사용합니다: 하나는 바깥쪽 클래스를 위한 것이고, 다른 하나는 자신을 위한 것입니다:
template<typename T1>
struct string
{
// 멤버 템플릿 함수
template<typename T2>
int compare(const T2&);
// 생성자도 템플릿이 될 수 있음
template<typename T2>
string(const std::basic_string<T2>& s) { /*...*/ }
};
// 클래스 외부에서의 string<T1>::compare<T2> 정의
template<typename T1> // 외부 클래스 템플릿용
template<typename T2> // 멤버 템플릿용
int string<T1>::compare(const T2& s) { /* ... */ }
멤버 함수 템플릿
소멸자
와
복사 생성자
는 템플릿이 될 수 없습니다. 복사 생성자의 타입 시그니처로 인스턴스화될 수 있는 템플릿 생성자가 선언된 경우, 대신
암시적으로 선언된 복사 생성자
가 사용됩니다.
멤버 함수 템플릿은 virtual이 될 수 없으며, 파생 클래스의 멤버 함수 템플릿은 기본 클래스의 virtual 멤버 함수를 재정의할 수 없습니다.
class Base
{
virtual void f(int);
};
struct Derived : Base
{
// 이 멤버 템플릿은 Base::f를 재정의하지 않습니다
template<class T> void f(T);
// 비템플릿 멤버 재정의는 템플릿을 호출할 수 있습니다:
void f(int i) override
{
f<>(i);
}
};
템플릿이 아닌 멤버 함수와 동일한 이름을 가진 템플릿 멤버 함수를 선언할 수 있습니다. 충돌이 발생하는 경우(어떤 템플릿 특수화가 비템플릿 함수 시그니처와 정확히 일치할 때), 해당 이름과 타입의 사용은 명시적 템플릿 인수 목록이 제공되지 않는 한 비템플릿 멤버를 참조합니다.
template<typename T>
struct A
{
void f(int); // 비템플릿 멤버
template<typename T2>
void f(T2); // 멤버 템플릿
};
// 템플릿 멤버 정의
template<typename T>
template<typename T2>
void A<T>::f(T2)
{
// 일부 코드
}
int main()
{
A<char> ac;
ac.f('c'); // 템플릿 함수 A<char>::f<char>(char) 호출
ac.f(1); // 비템플릿 함수 A<char>::f(int) 호출
ac.f<>(1); // 템플릿 함수 A<char>::f<int>(int) 호출
}
멤버 함수 템플릿의 클래스 외부 정의는 반드시 클래스 내부 선언과
동등(equivalent)
해야 합니다(동등성의 정의는
함수 템플릿 오버로딩
참조). 그렇지 않으면 오버로드로 간주됩니다.
struct X
{
template<class T> T good(T n);
template<class T> T bad(T n);
};
template<class T> struct identity { using type = T; };
// 정상: 동등한 선언
template<class V>
V X::good(V n) { return n; }
// 오류: X 내부의 선언과 동등하지 않음
template<class T>
T X::bad(typename identity<T>::type n) { return n; }
변환 함수 템플릿
사용자 정의
변환 함수
는 템플릿이 될 수 있습니다.
struct A
{
template<typename T>
operator T*(); // 모든 타입에 대한 포인터로의 변환
};
// 클래스 외부 정의
template<typename T>
A::operator T*() { return nullptr; }
// char*에 대한 명시적 특수화
template<>
A::operator char*() { return nullptr; }
// 명시적 인스턴스화
template A::operator void*();
int main()
{
A a;
int* ip = a.operator int*(); // A::operator int*()에 대한 명시적 호출
}
오버로드 해결
과정에서 변환 함수 템플릿의 특수화들은
이름 검색
을 통해 발견되지 않습니다. 대신, 모든 가시적인 변환 함수 템플릿들이 고려되며,
템플릿 인자 추론
(변환 함수 템플릿에 대한 특별한 규칙을 가짐)을 통해 생성된 모든 특수화는 이름 검색으로 발견된 것처럼 사용됩니다.
파생 클래스의 using 선언은 기본 클래스의 템플릿 변환 함수 특수화를 참조할 수 없습니다.
|
사용자 정의 변환 함수 템플릿은 추론된 반환 타입을 가질 수 없습니다:
struct S
{
operator auto() const { return 10; } // OK
template<class T> operator auto() const { return 42; } // error
};
|
(C++14부터)
|
멤버 변수 템플릿
변수 템플릿 선언은 클래스 범위에서 나타날 수 있으며, 이 경우 정적 데이터 멤버 템플릿을 선언합니다. 자세한 내용은
variable templates
를 참조하십시오.
|
(C++14부터)
|
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
|
DR
|
적용 대상
|
게시된 동작
|
올바른 동작
|
|
CWG 1878
|
C++14
|
operator auto가 기술적으로 허용됨
|
operator auto 금지됨
|