템플릿 디버깅

템플릿 인스턴스화 결과 확인



인스턴스화된 함수 이름 출력

  • __FUNCTION__: 함수 이름만 출력 (표준 매크로)
  • __FUNCSIG__: 함수 시그니처 포함 (비표준, MSVC 전용)
  • __PRETTY_FUNCTION__: 함수 시그니처 포함 (비표준, g++, clang)
  • std::source_location: C++20부터 지원
  #include <source_location>

std::source_location s = std::source_location::current();
  

템플릿 구현

템플릿과 파일 분할

  • 템플릿은 헤더파일에 구현해야 함.
  • 구현부가 다른 파일에 있으면 컴파일 실패.

함수 템플릿과 오버로딩

  // 기본 함수
int square(int a) { return a * a; }

// 함수 템플릿
template<typename T>
T square(T a) { return a * a; }

// 호출 예시
square<int>(3); // 명시적 인스턴스화
square(3);     // 타입 추론
  

타입에 따라 다른 구현 (requires)

  template<typename T>
requires std::integral<T>
T square(T a) { return a * a; }
  

템플릿 인자가 2개인 경우

  • 반환타입 표기법:
    • auto + decltype
    • type_traitsstd::common_type
    • C++20에서는 all auto 사용 가능
  // C++11
template<typename T1, typename T2>
auto add(const T1& a, const T2& b) -> decltype(a + b)
{
    return a + b;
}

// C++11 - type_traits
template<typename T1, typename T2>
typename std::common_type<T1, T2>::type add(const T1& a, const T2& b)
{
    return a + b;
}

// C++20
auto add(const auto& a, const auto& b)
{
    return a + b;
}
  

템플릿 생성 (C++20)

C++20부터는 template 대신에 아래와 같이 진행도 가능

  auto square(auto a) { return a * a; }
  

클래스 템플릿

  • 클래스 템플릿도 헤더파일에 구현해야 함.
  template<typename T>
class Point
{
    T x, y;
public:
    Point(T a, T b) : x(a), y(b) {}
};
  

template static 멤버 변수

다음과 같이 외부에서 따로 초기화 진행

  template<typename T1, typename T2>
struct Object
{
    T1 first;
    T2 second;
    static int cnt;
}

template<typename T1, typename T2>
int Object<T1, T2>::cnt = 0; // non-const static member in-class initialization 
  

추가 템플릿 종류

Member Template

  • 멤버 함수를 템플릿으로 만드려면 클래스 안에 template표기 넣어서 만들어야함
  • 외부에 구현하면 모양이 조금 복잡
  template<typename T>
struct Object
{
    void mf1(T n) {}
    template<typename U>
    void mf2(U n) {}
}

// 외부 구현 시
template<typename T> template<typename U>
void Object<T>::mf2(U n) {}
  

필요성

  • Coercion by Member Template
    • T가 U로 복사(대입) 가능하다면, Point<T>도 Point<U>로 복사(대입) 가능해야한다.
    • 다른 타입 객체의 멤버에 접근하기 위해 friend도 필요
      • Point는 template 인자가 한개인 애인데 애들끼리 다 friend
    • 타입에 대한 제한도 필요하므로 requires is_convertible_v<U, T> 사용
  template<typename T>
class Point
{
    T x, y;
    public:
        Point(const T& a, const T& b): x(a), y(b)

        template<typename U>
        requires std::is_convertible_v<U, T>
        Point(const Point<U>& p): x(p.x), y(p.y) {
            template<typename> friend class Point;
        }
}

int main()
{
    Point<std:;string> p1("1", "2");
    Point<int> p2 = p1;
}
  

Variable Template (C++14)

  • 예시
      template<typename T>
    constexpr T pi = static_cast<T>(3.141592);
    int main()
    {
        double area1 = 3*3*pi<double>;
    }
      
  • 필요성
    • template specialization과 같이 사용
    • type_traits 구현의 핵심
    • STL 구현에 다양하게 활용

Using Template (C++11)

  • 예시
    • 위쪽 대신 아래쪽으로 활용가능
      using SET = std::unordered_set<int>;
    using SETD = std::unordered_set<double>;
    template<typename T>
    using SET = std::unorderd_set<T>;
      

lazy instantiation

  • 개념
    • 클래스 템플릿의 멤버함수는 사용된 것만 인스턴스화 됨
    • 그래서 사용안한 함수에 에러가 있어도 컴파일
  • 재밌는 사례
    • queue 구현 시 vector는 pop_front()가 없기에 사용하면 안되는데 실제로 사용해도 pop()을 쓰기 전 까지는 에러가 안 남
  • static member
    • static도 마찬가지로 규칙을 적용받음

class template & friend

  • 문제
    • operator «를 friend로 등록해놓고 외부에 구현해놓으면 Instantiate될 때 (예를 들어 int) operator« (os, const Point& pt) 버전이 딸려들어감
    • 이러면 외부에 구현된 템플릿화된 operator«와 연결되지않아서 에러
    • friend함수를 그냥 안에 구현하면 해결
    • 외부함수로 쓰려면 다른 템플릿 변수 활용해 템플릿 함수로 만듬
  template<typename T>
class Point
{
    T x, y;
public:
    Point(T a, T b) : x(a), y(b) {}

    // 아래와 같이 선언만 해두면 문제 상황 발생
    // friend std::ostream& operator<<(std::ostream& os, const Point<T>& pt)

    friend std::ostream& operator<<(std::ostream& os, const Point<T>& pt)
    {
        std::cout << pt.x << ", " << pt.y << std::endl;
        return os;
    }
}

template<typename T>
std::ostream& operator<<(std::ostream& os, const Point<T>& pt)
{
    std::cout << pt.x << ", " << pt.y << std::endl;
    return os;
}