Language/C++

📌 Chapter 12 - 템플릿

e-cko 2025. 10. 24. 17:14
반응형
반응형

1. 함수 템플릿 (Function Template)

· 템플릿 ''이라는 의미이다.
·
함수 템플릿은 함수의 동작(알고리즘)은 고정되어 있고, 실제 자료형은 호출 시점에 결정한다.
·
문법은 template <typename T> 또는 template <class T> 형태를 사용한다.
· typename
class는 템플릿 매개변수 선언에서 동일한 의미를 사용한다.
·
템플릿 매개변수 이름은 T 외의 다른 이름으로 변경해서 사용할 수 있다.
·
컴파일러는 함수 템플릿으로부터 실제 타입에 맞춘 함수를 생성(인스턴스화) 한다.

상세 보충
1️
컴파일 시점에 타입별로 함수가 생성된다.
2️
한 번 생성된 함수는 동일한 시그니처에 대해 재생성하지 않으므로 실행 시 성능 저하는 없다.
3️
템플릿 인스턴스화 때문에 컴파일 시간이 늘어날 수 있다.

1) 호출 방식 및 타입 추론

· 템플릿은 명시적 인스턴스화 (add<int>(1,2)) 또는 타입 추론 (add(1.1, 2.2))로 호출한다.
·
타입 추론은 전달한 인자의 타입을 기준으로 컴파일러가 가장 적절한 타입을 결정한다.
·
전달 인자 간에 자동 형 변환이 필요하면 그에 따라 타입이 결정되므로 의도치 않은 변환(: narrowing) 에 주의한다.

2) 템플릿과 일반 함수의 관계

· 일반 함수(-템플릿)템플릿 함수가 함께 정의되어 있을 때, 정확히 일치하는 일반 함수가 있으면 일반 함수가 우선 호출된다.
·
이 규칙 때문에 템플릿을 사용할 때는 오버로딩 우선순위를 반드시 고려한다.

자주 하는 실수 ⚠️

· ⚠️ 문자열 리터럴(char const*) char* 타입을 혼동해서 특수화나 오버로드가 엉키는 실수를 한다.
·
⚠️ 타입 추론으로 인해 정수 리터럴과 실수 리터럴의 혼용에서 의도치 않은 타입이 선택되는 실수를 한다.
·
⚠️ 템플릿으로 모든 것을 처리하려다가 부분 특수화(특히 함수 부분 특수화는 불가) 와 같은 규칙을 놓치는 실수를 한다.
·
항상 정확한 타입 매개변수 명시 또는 명시적 형변환으로 의도를 분명히 한다.

4) 예시

· template<typename T> T add(T x, T y)는 같은 타입의 두 값을 더하는 일반적인 템플릿 함수이다.
· template<class T1, class T2> void getT(T1 x, T2 y)
는 서로 다른 두 타입을 받을 수 있는 템플릿 함수이다.


마무리 정리 (함수 템플릿에서 반드시 기억할 것)

1️ 템플릿은 타입에 따라 함수(또는 클래스)를 생성한다.
2️
명시적 인스턴스화타입 추론을 모두 사용할 수 있다.
3️
일반 함수가 존재하면 일반 함수가 우선 호출된다.
4️
부분 특수화는 클래스 템플릿에서만 사용한다(함수 템플릿은 부분 특수화 불가).


2. 특수화 (Specialization)

· 특수화(Specialization)는 템플릿의 기본 구현으로는 적절하지 않은 특정 타입에 대해 별도의 구현(예외 처리) 을 제공하는 기술이다.
·
특수화는 완전 특수화(Full Specialization) 부분 특수화(Partial Specialization) 개념으로 나뉜다.
·
함수 템플릿은 완전 특수화만 허용하고, 부분 특수화는 지원하지 않는다.
·
클래스 템플릿은 부분 특수화를 지원하므로 클래스 템플릿에서 보다 세밀한 특수화를 적용한다.

1) 특수화 문법 예시 해설

· 기본 템플릿 구현은 일반적인 비교 연산 등에서 사용한다.
·
문자열(const char*)과 같은 경우 포인터 값 비교가 아니라 문자열 내용 비교를 해야 하므로 특수화로 처리한다.
·
특수화 선언은 template <> 뒤에 특정 타입에 대한 함수 정의를 작성한다.

예시 형태(문법):
· template<> ReturnType Func<SpecificType>(SpecificType a, SpecificType b)
형태로 완전 특수화를 작성한다.
·
또는 template<> ReturnType Func(SpecificType a, SpecificType b) 형태로 간단히 쓸 수 있다(컴파일러가 타입을 유추함).

2) 실전에서 주의할 점 ⚠️

· ⚠️ char* const char* 의 차이를 반드시 구분한다. 문자열 리터럴은 const char* 타입이므로 특수화 시 타입을 일치시켜야 한다.
·
⚠️ 함수 템플릿의 특수화는 오버로드와 충돌 가능하므로 오버로드 우선순위를 확인해야 한다.
·
⚠️ 특수화 구현에서 사용하는 라이브러리 함수(: strcmp) <cstring>을 포함해서 사용한다.
·
문자열 비교가 필요하면 문자열 내용 비교 함수를 사용하되, 널 종료 안전성문자열 수명(lifetime) 에 유의한다.

3) 예시: 일반 Min 템플릿과 const char* 특수화 설명

· 기본 템플릿은 operator< 로 비교하는 일반적인 구현을 사용한다.
· const char*
전용 특수화는 strcmp 를 사용하여 문자열 내용을 비교하는 별도 구현을 제공한다.
·
이때 반환 타입과 매개변수 타입의 const 일치성을 맞추는 것이 중요하다.


마무리 정리 (특수화에서 반드시 기억할 것)

1️ 특수화는 특정 타입에 대한 예외 구현을 제공한다.
2️
함수 템플릿은 완전 특수화만 가능하고 부분 특수화는 클래스 템플릿에서만 사용한다.
3️
문자열은 포인터 비교가 아닌 내용 비교를 해야 하므로 const char* 특수화가 필요하다.
4️
오버로드와 특수화의 우선순위를 항상 확인한다.


💻 예제 코드 (C++ 예제, 각 라인에 주석 포함)

#include <iostream>     // 입출력 스트림을 사용한다.

#include <cstring>      // strcmp를 사용하기 위해 포함한다.

 

using namespace std;    // std:: 접두사를 생략하기 위해 사용한다.

 

// 기본 템플릿: 같은 타입의 두 값을 더한다.

template <typename T>

T add(T x, T y) {               // 템플릿 함수 선언이다.

    return x + y;               // 두 값을 더한 결과를 반환한다.

}

 

// 명시적 일반 함수 오버로드: int 전용 일반 함수이다.

int add(int x, int y) {         // 일반 함수 정의이다.

    return x + y;               // int 덧셈 결과를 반환한다.

}

 

// 템플릿 예제: 서로 다른 타입 두 개를 출력한다.

template <class T1, class T2>

void getT(T1 x, T2 y) {         // 두 타입을 받는 템플릿 함수이다.

    cout << x << ", " << y << endl; // 값을 출력한다.

}

 

// 일반적인 Min 템플릿: operator<를 사용해서 더 작은 값을 반환한다.

template <typename T>

T Min(T a, T b) {                // 일반 템플릿 Min 정의이다.

    return (a < b) ? a : b;     // 비교 결과에 따라 작은 값을 반환한다.

}

 

// const char*에 대한 완전 특수화: 문자열 내용을 비교한다.

template <>

const char* Min<const char*>(const char* a, const char* b) { // 특수화 정의이다.

    return (strcmp(a, b) < 0) ? a : b; // strcmp로 내용을 비교해서 반환한다.

}

 

int main() {                     // 프로그램 진입점이다.

    cout << add<int>(1, 2) << endl;  // 명시적 템플릿 인자 사용한다. (일반 함수 우선주의로 일반함수가 호출된다)

    cout << add(1.1, 2.2) << endl;   // 타입 추론을 사용한다.

    cout << getT<short, int>(1, 2) << endl; // 서로 다른 타입 사용 예이다.

 

    const char* s1 = "apple";    // 문자열 리터럴은 const char* 타입이다.

    const char* s2 = "banana";   // 문자열 리터럴이다.

    cout << Min(s1, s2) << endl; // const char* 특수화가 호출되어 내용 비교를 수행한다.

 

    return 0;                    // 정상 종료한다.

}

예제 실행 결과 (예상)

· 첫 줄: 3 출력이다.
·
두 번째 줄: 3.3 출력이다.
·
세 번째 줄: 1, 2 출력이다.
·
네 번째 줄: apple 또는 banana 중 사전 순으로 앞서는 문자열이 출력된다(: apple)이다.


추가 팁(초보자용)

· 템플릿을 설계할 때는 타입 일관성(const 포함 여부 등) 을 먼저 결정한다.
·
문자열 비교가 필요하면 포인터 비교가 아닌 내용 비교를 명확히 구현한다.
·
오버로딩과 특수화가 혼재한 상황에서는 명시적 타입 지정으로 컴파일러 선택을 유도한다.
·
⛏️ 디버깅 시에는 컴파일러가 생성한 에러 메시지에 나타나는 인스턴스화된 템플릿 시그니처를 확인한다.

반응형