템플릿
템플릿 (Templates)
타입을 인자로 받아 "타입들의 가족"을 찍어내는 방법이 C++의 **템플릿(template)**이에요. 템플릿은 다음 중 하나를 정의하는 C++ 엔티티예요.
- 클래스들의 가족(클래스 템플릿) — 중첩 클래스일 수도 있어요.
- 함수들의 가족(함수 템플릿) — 멤버 함수일 수도 있어요.
- 타입들의 가족에 대한 별명(별칭 템플릿, alias template). (C++11부터)
- 변수들의 가족(변수 템플릿, variable template). (C++14부터)
- 한 개념(제약과 컨셉, constraints and concepts). (C++20부터)
템플릿은 하나 이상의 **템플릿 매개변수(template parameter)**로 매개변수화되는데, 매개변수에는 세 종류가 있어요. 타입 템플릿 매개변수, 비타입 템플릿 매개변수, 템플릿 템플릿 매개변수.
템플릿 인자(template argument)가 제공되고, 또는 함수 및(C++17부터) 클래스 템플릿의 경우 추론되면, 그것들이 템플릿 매개변수에 치환되어 **템플릿 특수화(template specialization)**를 얻어요. 즉 구체적인 타입이나 구체적인 함수 lvalue를 얻는 거예요.
특수화는 명시적으로 제공될 수도 있어요. 전체 특수화(full specialization)는 클래스, 변수(C++14부터), 함수 템플릿에 허용되고, 부분 특수화(partial specialization)는 클래스 템플릿과 변수 템플릿(C++14부터)에만 허용돼요.
클래스 템플릿 특수화가 완전한 객체 타입을 요구하는 문맥에서 참조되거나, 함수 템플릿 특수화가 함수 정의의 존재를 요구하는 문맥에서 참조될 때, 템플릿은 **인스턴스화(instantiated)**돼요(그 코드가 실제로 컴파일됨). 물론 그 템플릿이 이미 명시적으로 특수화되거나 명시적으로 인스턴스화된 경우는 예외예요. 클래스 템플릿의 인스턴스화는, 어떤 멤버 함수도 사용되지 않는다면, 그 멤버 함수들을 인스턴스화하지 않아요. 링크 시점에 서로 다른 번역 단위가 생성한 동일한 인스턴스화는 병합돼요.
클래스 템플릿의 정의는 암시적 인스턴스화 지점에서 보여야 해요. 그래서 템플릿 라이브러리는 보통 모든 템플릿 정의를 헤더에 제공해요(예를 들어 대부분의 Boost 라이브러리는 header-only예요).
출처: cppreference
본문
문법 (Syntax)
| 표기 | 의미 |
|---|---|
template < parameter-list > requires-clause(선택) declaration |
(1) |
export template < parameter-list > declaration |
(2) (C++11까지) |
template < parameter-list > concept concept-name = constraint-expression ; |
(3) (C++20부터) |
- parameter-list — 템플릿 매개변수들의 비어 있지 않은 쉼표 구분 목록. 각각은 비타입 매개변수, 타입 매개변수, 템플릿 매개변수, 또는 그중 어떤 것의 매개변수 팩이에요. (C++11부터)
- requires-clause — (C++20부터) 템플릿 인자에 대한 제약을 지정하는 requires-clause.
- declaration — 클래스(struct와 union 포함), 멤버 클래스나 멤버 열거형 타입, 함수나 멤버 함수, 이름공간 스코프의 정적 데이터 멤버, 클래스 스코프의 변수나 정적 데이터 멤버(C++14부터), 또는 별칭 템플릿(C++11부터)의 선언. 템플릿 특수화를 정의할 수도 있어요.
- concept-name / constraint-expression — 제약과 컨셉(Constraints and concepts) 참조.
(2)의 export는 템플릿을 exported(내보내짐)로 선언하는 선택적 수식자였어요(클래스 템플릿에 쓰면 그 모든 멤버도 내보내진 것으로 선언됐어요). 내보내진 템플릿을 인스턴스화하는 파일은 그 정의를 포함할 필요가 없었어요. 선언만으로 충분했거든요. export의 구현은 드물었고 세부 사항에서 서로 일치하지 않았어요. (C++11까지)
템플릿 식별자 (Template identifiers)
템플릿 식별자는 다음 문법 중 하나를 가져요.
| 표기 | 의미 |
|---|---|
template-name < template-argument-list(선택) > |
(1) |
operator op < template-argument-list(선택) > |
(2) |
operator "" identifier < template-argument-list(선택) > |
(3) (C++11부터, 비권장) |
operator user-defined-string-literal < template-argument-list(선택) > |
(4) (C++11부터) |
- 단순 템플릿 식별자.
- 연산자 함수 템플릿 식별자. 3,4) 리터럴 연산자 함수 템플릿 식별자.
- template-name — 템플릿의 이름인 식별자
- op — 오버로드 가능한 연산자
- identifier — 식별자
- user-defined-string-literal —
""뒤에 식별자가 이어지는 것
클래스 템플릿 특수화의 이름인 단순 템플릿 식별자는 클래스를 가리켜요.
별칭 템플릿 특수화의 이름인 템플릿 식별자는 타입을 가리켜요.
함수 템플릿 특수화의 이름인 템플릿 식별자는 함수를 가리켜요.
모든 다음 조건이 충족되면, 템플릿 식별자는 **유효(valid)**해요.
- 인자가 매개변수 수보다 많지 않거나, 매개변수가 템플릿 매개변수 팩이다. (C++11부터)
- 기본 템플릿 인자가 없는 각 추론 불가능·비팩(C++11부터) 매개변수에 대해 인자가 있다.
- 각 템플릿 인자가 대응하는 템플릿 매개변수와 일치한다.
- 각 템플릿 인자를 다음 템플릿 매개변수(있다면)에 치환하는 것이 성공한다.
- (C++20부터) 템플릿 식별자가 비의존적이라면, 연관된 제약들이 아래에 지정된 대로 충족된다.
유효하지 않은 단순 템플릿 id는 컴파일 타임 오류예요. 다만 함수 템플릿 특수화를 가리키는 경우는 예외인데, 그때는 SFINAE가 적용될 수 있어요.
template<class T, T::type n = 0>
class X;
struct S
{
using type = int;
};
using T1 = X<S, int, int>; // error: 인자가 너무 많음
using T2 = X<>; // error: 첫 번째 템플릿 매개변수에 기본 인자가 없음
using T3 = X<1>; // error: 값 1이 타입 매개변수와 일치하지 않음
using T4 = X<int>; // error: 두 번째 템플릿 매개변수에 대한 치환 실패
using T5 = X<S>; // OK
(C++20부터) 단순 템플릿 id의 템플릿 이름이 제약된(constrained) 비함수 템플릿이나 제약된 템플릿 템플릿 매개변수를 가리키는데, 알려지지 않은 특수화의 멤버인 멤버 템플릿이 아니고, 단순 템플릿 id의 모든 템플릿 인자가 비의존적이라면, 그 제약된 템플릿의 연관 제약이 충족되어야 해요.
template<typename T>
concept C1 = sizeof(T) != sizeof(int);
template<C1 T>
struct S1 {};
template<C1 T>
using Ptr = T*;
S1<int>* p; // error: 제약이 충족되지 않음
Ptr<int> p; // error: 제약이 충족되지 않음
template<typename T>
struct S2 { Ptr<int> x; }; // error, 진단은 요구되지 않음
template<typename T>
struct S3 { Ptr<T> x; }; // OK, 충족이 요구되지 않음
S3<int> x; // error: 제약이 충족되지 않음
template<template<C1 T> class X>
struct S4
{
X<int> x; // error, 진단은 요구되지 않음
};
template<typename T>
concept C2 = sizeof(T) == 1;
template<C2 T> struct S {};
template struct S<char[2]>; // error: 제약이 충족되지 않음
template<> struct S<char[2]> {}; // error: 제약이 충족되지 않음
모든 다음 조건이 충족되면, 두 템플릿 식별자는 같음(same).
- 그들의 template-name이나 연산자가 같은 템플릿을 가리킨다.
- 그들의 대응하는 타입 템플릿 인자가 같은 타입이다.
- 그들의 대응하는 비타입 템플릿 인자에 의해 결정되는 템플릿 매개변수 값이 template-argument-equivalent이다.
- 그들의 대응하는 템플릿 템플릿 인자가 같은 템플릿을 가리킨다.
같은 두 템플릿 식별자는 같은 변수(C++14부터), 클래스, 또는 함수를 가리켜요.
템플릿화된 엔티티 (Templated entity)
템플릿화된 엔티티(templated entity)("temploid"라고도 불려요)는 템플릿 정의 안에서 정의되는(또는 람다 표현식의 경우 생성되는, C++11부터) 어떤 엔티티예요. 다음 모두가 템플릿화된 엔티티예요.
- 템플릿
- (C++26부터) 확장 문장(expansion statement)의 item-declaration이나 compound-statement 안에서 정의되거나 생성된 엔티티
- 템플릿화된 엔티티 안에서 정의되거나 생성된 엔티티(지역 클래스, 지역·블록 변수, friend 함수 등)
- 템플릿화된 엔티티의 멤버(클래스 템플릿의 비템플릿 멤버 함수 등)
- 템플릿화된 엔티티인 열거형의 열거자(enumerator)
- (C++11부터) 템플릿화된 엔티티의 선언에 나타나는 람다 표현식의 클로저 타입(closure type)
예를 들어 아래에서 함수 A::f는 함수 템플릿이 아니지만, 그래도 템플릿화된 것으로 간주돼요.
template<typename T>
struct A
{
void f() {}
};
**템플릿화된 함수(templated function)**는 함수 템플릿이거나 템플릿화된 함수예요.
**템플릿화된 클래스(templated class)**는 클래스 템플릿이거나 템플릿화된 클래스예요.
(C++14부터) **템플릿화된 변수(templated variable)**는 변수 템플릿이거나 템플릿화된 변수예요.
키워드 (Keywords)
template, export
결함 보고 (Defect reports)
다음의 동작 변경 결함 보고가 이전에 공개된 C++ 표준들에 소급 적용됐어요.
| DR | 적용 | 공개된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 2293 | C++98 | 템플릿 식별자가 유효한지 결정하는 규칙이 제공되지 않았음 | 제공 |
| CWG 2682 | C++98/C++14 | 템플릿화된 함수/템플릿 클래스(C++98)/템플릿화된 변수(C++14)의 정의가 빠져 있었음 | 추가 |
| P2308R1 | C++98 | 두 템플릿 식별자는 대응하는 비타입 템플릿 인자가 template-argument-equivalent가 아니면 서로 달랐음 | 대응하는 비타입 템플릿 매개변수 값이 template-argument-equivalent가 아니면 서로 다름 |
같이 보기 (See also)
- C 문서의 Generic selection
더 알아보기
- 클래스 템플릿: 타입들의 가족을 정의하는 방법.
- 함수 템플릿: 함수들의 가족을 정의하는 방법.
- 템플릿 매개변수와 인자: 템플릿을 매개변수화하는 방법.
- 템플릿 특수화: 특정 타입에 대해 기존 템플릿을 맞춤.
- 부분 특수화 (Partial specialization): 일부 템플릿 인자만 고정해 특수화.
- 매개변수 팩 (C++11): 템플릿에서 타입 목록을 사용하는 방법.