Programming/C++

C++ : 고급 템플릿

나무수피아 2025. 5. 22. 01:15
728x90
반응형
고급 템플릿 프로그래밍

고급 템플릿 프로그래밍

1. SFINAE, enable_if

SFINAE (Substitution Failure Is Not An Error)는 C++ 템플릿 프로그래밍에서 매우 핵심적인 개념입니다. 이는 템플릿을 인스턴스화할 때 특정 조건이 충족되지 않으면 단순히 해당 템플릿을 무시하게 만드는 메커니즘입니다. `enable_if`는 SFINAE를 구현하는 도구로서, 특정 타입 조건을 만족할 때만 템플릿 함수를 활성화하도록 돕습니다.

1.1. SFINAE

SFINAE는 오버로딩된 템플릿 함수 중 일부가 특정 타입에 맞지 않더라도 컴파일 에러로 간주되지 않게 하여 더 유연하고 조건적인 템플릿 설계를 가능하게 합니다.

#include <iostream>
#include <type_traits>

template <typename T>
std::enable_if_t<std::is_integral<T>::value, T> add(T a, T b) {
    return a + b;
}

int main() {
    std::cout << add(3, 4) << std::endl;      // 7 출력됨
    // std::cout << add(3.14, 2.71) << std::endl;  // 컴파일 오류 발생
    return 0;
}

1.2. enable_if

`std::enable_if`는 타입 조건이 true일 때만 해당 템플릿을 정의하도록 합니다. 이를 통해 함수 템플릿의 사용을 제어하고, 코드의 타입 안정성을 높일 수 있습니다.

#include <iostream>
#include <type_traits>

template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type add(T a, T b) {
    return a + b;
}

int main() {
    std::cout << add(5, 10) << std::endl; // 출력: 15
    return 0;
}

2. 템플릿 메타프로그래밍

템플릿 메타프로그래밍(Template Metaprogramming)은 C++ 템플릿을 활용하여 컴파일 타임에 계산과 로직 처리를 수행하는 기법입니다. 이 기법은 성능을 향상시키고, 코드 최적화를 가능하게 해줍니다. 특히 컴파일 타임 상수 계산, 타입 선택, 조건 분기 등에 자주 사용됩니다.

2.1. 컴파일 타임 계산

대표적인 예로, 팩토리얼을 컴파일 타임에 계산하는 재귀 템플릿을 들 수 있습니다. 이 방식은 런타임 오버헤드 없이 상수값을 생성합니다.

#include <iostream>

template <int N>
struct factorial {
    static constexpr int value = N * factorial<N - 1>::value;
};

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

int main() {
    std::cout << factorial<5>::value << std::endl; // 출력: 120
    return 0;
}
💡 참고: constexpr을 통해 C++11 이후에는 함수도 컴파일 타임 계산이 가능해졌지만, 템플릿을 이용한 계산은 여전히 강력한 메타프로그래밍 기법입니다.

3. type_traits, std::variant, std::any

고급 템플릿 프로그래밍에서 C++ 표준 라이브러리에서 제공하는 `type_traits`, `std::variant`, `std::any`는 매우 유용한 도구입니다. 이들은 타입 기반 조건 처리, 다형성 처리, 타입 안전성을 향상시키는 데 기여합니다.

3.1. type_traits

`type_traits`는 타입 정보를 템플릿 기반으로 판단하고, 조건 분기를 가능하게 합니다. 예를 들어 특정 타입이 정수형인지, 포인터인지, 상수형인지 등을 판단할 수 있습니다.

#include <iostream>
#include <type_traits>

template <typename T>
void printIfIntegral(T t) {
    if constexpr (std::is_integral<T>::value) {
        std::cout << t << std::endl;
    } else {
        std::cout << "Not an integral type!" << std::endl;
    }
}

int main() {
    printIfIntegral(42);     // 출력: 42
    printIfIntegral(3.14);   // 출력: Not an integral type!
    return 0;
}

3.2. std::variant

`std::variant`는 여러 타입 중 하나만을 가질 수 있는 타입 안전한 유니온입니다. 이를 통해 다양한 타입의 값을 하나의 변수로 처리할 수 있으며, `std::visit`을 통해 안전하게 값에 접근할 수 있습니다.

#include <iostream>
#include <variant>
#include <string>

std::variant<int, double, std::string> getData(int choice) {
    if (choice == 0) return 42;
    else if (choice == 1) return 3.14;
    else return "Hello";
}

int main() {
    auto data = getData(2);
    std::visit([](auto&& arg) {
        std::cout << arg << std::endl;
    }, data);
    return 0;
}
🔧 Tip: `std::variant`는 C++17 이상에서 사용할 수 있으며, switch문보다 훨씬 강력하고 타입 안전한 분기처리를 제공합니다.

3.3. std::any

`std::any`는 모든 타입을 저장할 수 있는 타입 안정 컨테이너입니다. 저장된 값을 꺼낼 때는 `std::any_cast`를 사용하며, 타입이 다르면 예외가 발생합니다.

#include <iostream>
#include <any>
#include <string>

int main() {
    std::any a = 42;
    std::cout << std::any_cast<int>(a) << std::endl; // 출력: 42

    a = std::string("Hello, C++!");
    std::cout << std::any_cast<std::string>(a) << std::endl; // 출력: Hello, C++!
    return 0;
}
⚠️ 주의: `std::any_cast`에서 타입이 일치하지 않으면 `std::bad_any_cast` 예외가 발생하므로, 항상 타입을 알고 있을 때 사용하는 것이 좋습니다.
728x90
반응형

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

C++ : 전문가 프로젝트  (65) 2025.05.26
C++ : 대규모 프로젝트 아키텍처  (45) 2025.05.25
C++ : 성능 최적화  (82) 2025.05.24
C++ : 스레드와 동기화  (60) 2025.05.23
C++ : 현대적 C++  (76) 2025.05.21
C++ : 스마트 포인터  (94) 2025.05.20
C++ : 고급 객체지향 설계  (94) 2025.05.19
C++ : 중급 프로젝트  (10) 2025.05.18