템플릿

2025. 3. 28. 08:47Programming Languages/C++

템플릿

템플릿은 C++의 가장 강력한 기능 중 하나로, 타입에 독립적인 코드를 작성할 수 있게 해줍니다. 템플릿을 사용하면 다양한 데이터 타입에 대해 동일한 알고리즘이나 클래스를 하나의 코드로 구현할 수 있어, 코드 재사용성과 유지보수성을 크게 향상시킬 수 있습니다.

7.1 함수 템플릿 기초

7.1.1 함수 템플릿의 개념

함수 템플릿은 다양한 데이터 타입에 대해 동작하는 함수의 "청사진"을 정의합니다. 실제 함수는 컴파일러가 템플릿으로부터 필요한 타입에 맞게 생성합니다.

#include <iostream>

// 두 값 중 최대값을 반환하는 함수 템플릿
template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    // 정수형에 대한 호출
    int iResult = max(10, 20);
    std::cout << "정수 최대값: " << iResult << std::endl;
    
    // 실수형에 대한 호출
    double dResult = max(3.14, 2.71);
    std::cout << "실수 최대값: " << dResult << std::endl;
    
    // 문자형에 대한 호출
    char cResult = max('A', 'Z');
    std::cout << "문자 최대값: " << cResult << std::endl;
    
    return 0;
}

위 코드에서:

  • template <typename T>: 템플릿 선언, T는 타입 매개변수
  • typename과 class 키워드 모두 타입 매개변수를 정의하는 데 사용할 수 있습니다.
  • 함수를 호출할 때, 컴파일러는 인수의 타입에 따라 적절한 함수 버전을 생성합니다.

7.1.2 템플릿 타입 추론

컴파일러는 함수 호출 시 전달된 인수의 타입을 기반으로 템플릿 매개변수의 타입을 자동으로 추론합니다:

#include <iostream>
#include <string>

template <typename T>
void showType(T value) {
    // typeid는 type_info 객체를 반환
    std::cout << "값: " << value << ", 타입: " << typeid(T).name() << std::endl;
}

int main() {
    showType(42);          // T는 int로 추론
    showType(3.14159);     // T는 double로 추론
    showType("Hello");     // T는 const char*로 추론
    showType(std::string("World")); // T는 std::string으로 추론
    
    return 0;
}

7.1.3 명시적 템플릿 인수

함수 호출 시 템플릿 타입을 명시적으로 지정할 수도 있습니다:

#include <iostream>

template <typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    // 명시적 타입 지정
    std::cout << max<int>(10, 20) << std::endl;
    std::cout << max<double>(3.14, 2.71) << std::endl;
    
    // 타입 변환을 통한 혼합 타입 사용
    std::cout << max<double>(10, 3.14) << std::endl; // 10은 double로 변환됨
    
    return 0;
}

7.1.4 여러 템플릿 매개변수

함수 템플릿은 여러 타입 매개변수를 가질 수 있습니다:

#include <iostream>
#include <string>

// 두 개의 타입 매개변수를 가진 함수 템플릿
template <typename T1, typename T2>
void printPair(T1 a, T2 b) {
    std::cout << "첫 번째 값(" << typeid(T1).name() << "): " << a << std::endl;
    std::cout << "두 번째 값(" << typeid(T2).name() << "): " << b << std::endl;
}

// 두 개의 서로 다른 타입을 처리하는 함수 템플릿
template <typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
    return a + b;
}

int main() {
    printPair(42, "Hello");              // T1=int, T2=const char*
    printPair(3.14, std::string("Pi"));  // T1=double, T2=std::string
    
    // 다른 타입의 덧셈
    auto result1 = add(5, 3.14);         // int + double = double
    auto result2 = add(std::string("Hello, "), "World"); // std::string + const char* = std::string
    
    std::cout << "result1: " << result1 << std::endl;
    std::cout << "result2: " << result2 << std::endl;
    
    return 0;
}

여기서 decltype(a + b)는 식 a + b의 타입을 추론하여 함수의 반환 타입으로 사용합니다. 후행 반환 타입(trailing return type) 구문 -> 타입은 C++11에서 도입되었습니다.

7.1.5 템플릿 특수화

특정 타입에 대해 다른 구현을 제공하고 싶을 때 템플릿 특수화를 사용할 수 있습니다:

#include <iostream>
#include <string>

// 일반 템플릿
template <typename T>
void printInfo(const T& value) {
    std::cout << "일반 템플릿: " << value << std::endl;
}

// char* 타입에 대한 특수화
template <>
void printInfo<char*>(char* const& value) {
    std::cout << "문자열 특수화: " << value << " (길이: " << strlen(value) << ")" << std::endl;
}

// const char* 타입에 대한 특수화
template <>
void printInfo<const char*>(const char* const& value) {
    std::cout << "상수 문자열 특수화: " << value << " (길이: " << strlen(value) << ")" << std::endl;
}

// std::string 타입에 대한 특수화
template <>
void printInfo<std::string>(const std::string& value) {
    std::cout << "std::string 특수화: " << value << " (길이: " << value.length() << ")" << std::endl;
}

int main() {
    int num = 42;
    printInfo(num);  // 일반 템플릿 사용
    
    char str[] = "Hello";
    printInfo(str);  // char* 특수화 사용
    
    const char* cstr = "World";
    printInfo(cstr);  // const char* 특수화 사용
    
    std::string sstr = "C++ Templates";
    printInfo(sstr);  // std::string 특수화 사용
    
    return 0;
}

템플릿 특수화는 일부 타입에 대해 최적화된 구현이나 특별한 처리가 필요할 때 유용합니다.

7.1.6 함수 템플릿 오버로딩

함수 템플릿은 일반 함수와 마찬가지로 오버로딩할 수 있습니다:

#include <iostream>

// 하나의 매개변수를 가진 템플릿
template <typename T>
void print(T value) {
    std::cout << "단일 값: " << value << std::endl;
}

// 두 개의 매개변수를 가진 템플릿
template <typename T>
void print(T value1, T value2) {
    std::cout << "두 값: " << value1 << ", " << value2 << std::endl;
}

// 다른 타입의 두 매개변수를 가진 템플릿
template <typename T1, typename T2>
void print(T1 value1, T2 value2) {
    std::cout << "서로 다른 타입의 두 값: " << value1 << ", " << value2 << std::endl;
}

// 비템플릿 함수 (일반 함수)
void print(int value) {
    std::cout << "특수한 int 처리: " << value << std::endl;
}

int main() {
    print(42);                // 일반 함수 호출 (더 특수한 버전이 우선)
    print(3.14);              // 템플릿 print<double>(T)
    print(10, 20);            // 템플릿 print<int>(T, T)
    print(10, 3.14);          // 템플릿 print<int, double>(T1, T2)
    print<double>(42);        // 명시적으로 템플릿 print<double>(T) 호출
    
    return 0;
}

함수 오버로딩 해결 규칙:

  1. 일치하는 비템플릿 함수(일반 함수)를 찾습니다.
  2. 일치하는 특수화된 템플릿 함수를 찾습니다.
  3. 가장 특수화된(가장 구체적인) 함수를 선택합니다.
  4. 모호한 경우 컴파일 오류가 발생합니다.

 

'Programming Languages > C++' 카테고리의 다른 글

비타입 템플릿 매개변수  (0) 2025.03.28
클래스 템플릿 기초  (0) 2025.03.28
챕터6. 실습 문제  (0) 2025.03.28
고급 포인터 주제  (0) 2025.03.28
참조  (0) 2025.03.28