member_template — 멤버 템플릿
member_template — 멤버 템플릿 (Member templates)
클래스, 구조체, 유니언의 멤버 명세 안에는 템플릿 선언(클래스 템플릿, 함수 템플릿, 변수 템플릿)이 나타날 수 있어요. 단, 지역 클래스(local class)는 예외예요. 이렇게 클래스 안에 정의된 템플릿을 **멤버 템플릿(member template)**이라고 불러요.
클래스의 멤버 함수가 여러 타입에 대해 동작해야 할 때, 함수 템플릿으로 만들면 타입마다 오버로드를 만들 필요 없이 하나로 처리할 수 있어요. 예를 들어 Printer 펑터처럼 어떤 타입이든 출력하게 만들 수 있죠.
출처: cppreference
본문
템플릿 선언(클래스, 함수, 변수 템플릿)은 지역 클래스가 아닌 어떤 클래스, 구조체, 유니언의 멤버 명세 안에 나타날 수 있어요.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
struct Printer
{
// 일반 펑터
std::ostream& os;
Printer(std::ostream& os) : os(os) {}
template<typename T>
void operator()(const T& obj) { os << obj << ' '; } // 멤버 템플릿
};
int main()
{
std::vector<int> v{1,2,3};
std::for_each(v.begin(), v.end(), Printer(std::cout));
std::string s{"abc"};
std::ranges::for_each(s, Printer(std::cout));
}
출력:
1 2 3 a b c
Printer::operator()가 함수 템플릿이라서 int와 char 모두 자연스럽게 처리할 수 있어요.
멤버 템플릿의 부분 특수화는 클래스 범위와 둘러싼 네임스페이스 범위 모두에 나타날 수 있어요. 명시적 특수화는 기본 템플릿이 나타날 수 있는 어떤 범위에서나 나타날 수 있어요.
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) { /* ... */ }
멤버 함수 템플릿
소멸자와 복사 생성자는 템플릿이 될 수 없어요. 복사 생성자의 타입 시그니처로 인스턴스화될 수 있는 템플릿 생성자가 선언되면, 암시적으로 선언된 복사 생성자가 대신 사용돼요.
멤버 함수 템플릿은 가상일 수 없고, 파생 클래스의 멤버 함수 템플릿은 기반 클래스의 가상 멤버 함수를 오버라이드할 수 없어요.
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)"해야 해요. 그렇지 않으면 오버로드로 간주돼요.
변환 함수(conversion function)는 템플릿일 수 있어요. 다만 오버로드 해석 시 변환 함수 템플릿의 특수화는 이름 조회로 찾아지지 않아요. 대신 모든 보이는 변환 함수 템플릿이 고려되고, 템플릿 인자 추론으로 만들어진 모든 특수화가 이름 조회로 찾은 것처럼 사용돼요.
C++14부터 사용자 정의 변환 함수 템플릿은 추론된 반환 타입을 가질 수 없어요.
멤버 변수 템플릿
변수 템플릿 선언이 클래스 범위에 나타날 수 있는데, 이 경우 정적 데이터 멤버 템플릿을 선언해요.