템플릿 메타프로그래밍

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

7.5 템플릿 메타프로그래밍

템플릿 메타프로그래밍(TMP)은 C++ 템플릿 시스템을 사용하여 컴파일 시간에 계산을 수행하는 기법입니다:

#include <iostream>
#include <type_traits>

// 컴파일 시간 팩토리얼 계산
template <int N>
struct Factorial {
    static constexpr int value = N * Factorial<N-1>::value;
};

// 재귀 종료 조건
template <>
struct Factorial<0> {
    static constexpr int value = 1;
};

// 컴파일 시간 제곱 계산
template <int N, int Power>
struct Power {
    static constexpr int value = N * Power<N, Power-1>::value;
};

// 재귀 종료 조건
template <int N>
struct Power<N, 0> {
    static constexpr int value = 1;
};

// 피보나치 수열 계산
template <int N>
struct Fibonacci {
    static constexpr int value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};

template <>
struct Fibonacci<0> {
    static constexpr int value = 0;
};

template <>
struct Fibonacci<1> {
    static constexpr int value = 1;
};

// 타입 특성 예제: 타입이 정수인지 확인
template <typename T>
struct IsInteger {
    static constexpr bool value = false;
};

template <>
struct IsInteger<int> {
    static constexpr bool value = true;
};

template <>
struct IsInteger<short> {
    static constexpr bool value = true;
};

template <>
struct IsInteger<long> {
    static constexpr bool value = true;
};

// 조건부 타입 선택
template <bool Condition, typename TrueType, typename FalseType>
struct Conditional {
    using type = TrueType;
};

template <typename TrueType, typename FalseType>
struct Conditional<false, TrueType, FalseType> {
    static_assert(false, "Bla");
    using type = FalseType;
};

int main() {
    // 컴파일 시간 계산
    std::cout << "5 팩토리얼: " << Factorial<5>::value << std::endl;
    std::cout << "2의 10승: " << Power<2, 10>::value << std::endl;
    std::cout << "피보나치(10): " << Fibonacci<10>::value << std::endl;
    
    // 타입 특성 사용
    std::cout << "int는 정수 타입인가? " << IsInteger<int>::value << std::endl;
    std::cout << "double은 정수 타입인가? " << IsInteger<double>::value << std::endl;
    
    // 조건부 타입 사용
    using SignedType = Conditional<true, int, unsigned>::type;
    using UnsignedType = Conditional<false, int, unsigned>::type;
    
    SignedType s = -42;
    UnsignedType u = 42;
    
    std::cout << "SignedType: " << s << std::endl;
    std::cout << "UnsignedType: " << u << std::endl;
    
    // C++11 이후의 표준 라이브러리 메타함수 사용
    std::cout << "int는 부동소수점 타입인가? " << std::is_floating_point<int>::value << std::endl;
    std::cout << "float는 부동소수점 타입인가? " << std::is_floating_point<float>::value << std::endl;
    
    return 0;
}

템플릿 메타프로그래밍의 주요 기법:

  • 컴파일 시간 계산 (상수, 팩토리얼, 피보나치 등)
  • 타입 특성 및 타입 메타함수
  • SFINAE(Substitution Failure Is Not An Error) 기법
  • 조건부 컴파일 및 타입 선택

C++11부터는 <type_traits> 헤더에서 다양한 표준 메타함수를 제공합니다.

7.5.1 SFINAE(Substitution Failure Is Not An Error)

SFINAE는 템플릿 인스턴스화 과정에서 치환 실패가 오류를 일으키지 않는 C++의 규칙입니다. 이를 활용하여 타입에 따른 함수 오버로딩을 제어할 수 있습니다:

#include <iostream>
#include <type_traits>
#include <vector>
#include <list>

// SFINAE를 사용한 함수 오버로딩 예제

// std::enable_if를 사용한 제약 조건 (C++11)
// 정수 타입일 때만 활성화되는 함수
template <typename T>
typename std::enable_if<std::is_integral<T>::value, void>::type
displayNumberInfo(T value) {
    std::cout << "정수 값: " << value << std::endl;
}

// 부동소수점 타입일 때만 활성화되는 함수
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type
displayNumberInfo(T value) {
    std::cout << "부동소수점 값: " << value << std::endl;
}

// C++17의 if constexpr를 사용한 간결한 구현
template <typename T>
void displayInfo(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "정수 값(C++17): " << value << std::endl;
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "부동소수점 값(C++17): " << value << std::endl;
    } else {
        std::cout << "다른 타입 값(C++17)" << std::endl;
    }
}

// 컨테이너의 크기를 출력하는 함수 (size() 메서드가 있는 경우)
template <typename Container>
auto printSize(const Container& c) -> decltype(c.size(), void()) {
    std::cout << "컨테이너 크기: " << c.size() << std::endl;
}

// size() 메서드가 없는 타입을 위한 대체 함수
void printSize(...) {
    std::cout << "크기를 확인할 수 없습니다." << std::endl;
}

int main() {
    // 정수 타입
    displayNumberInfo(42);
    
    // 부동소수점 타입
    displayNumberInfo(3.14);
    
    // C++17 스타일 구현
    displayInfo(42);
    displayInfo(3.14);
    displayInfo(std::string("Hello"));
    
    // SFINAE를 사용한 컨테이너 크기 출력
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::list<double> list = {1.1, 2.2, 3.3};
    std::string str = "Hello";
    int arr[5] = {1, 2, 3, 4, 5};
    
    printSize(vec);   // 벡터는 size() 메서드가 있음
    printSize(list);  // 리스트도 size() 메서드가 있음
    printSize(str);   // 문자열도 size() 메서드가 있음
    printSize(arr);   // 배열은 size() 메서드가 없으므로 두 번째 함수 호출
    printSize(42);    // 정수도 size() 메서드가 없으므로 두 번째 함수 호출
    
    return 0;
}

SFINAE를 활용한 코드 디자인:

  • 컴파일 시간에 타입 특성에 따라 적절한 함수 선택
  • 특정 기능이 있는 타입에 대해서만 동작하는 제네릭 코드 작성
  • 타입 안전성 향상
  • 명시적 오류 메시지 제공

C++20에서는 컨셉(Concepts)이라는 새로운 기능이 도입되어 SFINAE보다 더 명확하고 강력한 방식으로 템플릿 제약 조건을 표현할 수 있게 되었습니다.

 

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

STL (Standard Template Library)  (0) 2025.03.28
챕터7. 실습 문제  (0) 2025.03.28
가변 템플릿(Variadic Templates) (C++11)  (0) 2025.03.28
비타입 템플릿 매개변수  (0) 2025.03.28
클래스 템플릿 기초  (0) 2025.03.28