C++공부/C++ Templates

3. 형식이 아닌 템플릿 파라미터

아헿헿헿 2022. 6. 8. 11:37

3.1 형식이 아닌 클래스 템플릿 파라미터

template<typename T, std::size_t Maxsize>
class Stack {
  private:
    std::array<T,Maxsize> elems; // elements
    ...
};

다음과 같이 std::size_t 형태로 두번째 템플릿 파라미터를 받아와 사용됩니다. 이 Maxsize에 따라 각 템플릿 인스턴스는 자신만의 형식을 가지며, 다른 형식에 대해서는 암묵적이거나 명시적 형식 변환을 지원하지 않으면 일어나지 않습니다.

3.2 형식이 아닌 함수 템플릿 파라미터

함수 템플릿을 위해서도 형식이 아닌 템플릿 파라미터를 정의할 수 있습니다. 템플릿 파라미터가 이전 파라미터로부터 연역된다는 점을 명시할 수도 있습니다.

template<int Val, typename T>
T addValue (T x)
{
  return x + Val;
}

template<auto Val, typename T = decltype(Val)>
T foo();

3.3 형식이 아닌 템플릿 파라미터에 대한 제약

형식이 아닌 템플릿 파라미터에는 몇가지 제약 사항이 존재합니다.

  • 정수 상수형 값
  • 객체/함수/멤버에 대한 포인터
  • 객체나 함수에 대한 lvalue 참조자 또는 std::nullptr_t

인 경우에 사용이 가능합니다. double과 같은 부동소수점 숫자나 std::string과 같은 클래스형 객체는 형식이 아닌 템플릿 파라미터로 사용될 수 없습니다. 또한 포인터나 참조자를 템플릿 인자로 전달할 때 객체는 문자열 리터럴, 임시 객체나 데이터 멤버 혹은 다른 하위 객체여서는 안됩니다. C++ 버전에 따라 조금씩 제약 사항이 바뀝니다

  • C++11 : 외부 링크를 통한 객체 링크
  • C++14 : 외부나 내부 링크를 통한 객체 링크
  • C++17 : 링크 없어도 가능
template<char const* name>
class MyClass {};

MyClass<"hello"> x;             // Error!

extern char const s03[] = "hi"; // 외부 링크
char const s11[] = "hi";        // 내부 링크
Message<s03> m03;               // 모든 버전에서 OK
Message<s11> m11;               // C++11에서부터 OK
static char const s17[] = "hi"  // 링크 없음
Message<s17> m17;               // C++17에서부터 OK

유효하지 않은 표현식 피하기

형식이 아닌 템플릿 파라미터를 위한 인자는 컴파일 과정 표현식이기만 하면 됩니다.

template<int I, bool B>
class C;
...
C<sizeof(int) + 4, sizeof(int) == 4> c;

3.4 템플릿 파라미터 형식로서의 auto

C++17에서부터는 형식이 아닌 템플릿 파라미터로 허용된 어떠한 형식이든 받아들일 수 있게 정의할 수 있습니다.

template<typename T, auto Maxsize>
class Stack {};

심지어 template<decltype(auto) N>을 통해 N을 참조자로 인스턴스화할 수 있습니다.

3.5 요약

  • 템플릿은 형식이 아닌 값도 템플릿 파라미터가 될 수 있습니다.
  • 부동수서점 숫자나 클래스형 객체는 형식이 아닌 템플릿 파라미터를 위한 인자로 쓸 수 없습니다. 문자열 리터럴, 임시 객체, 하위 객체 에 대한 포인터나 참조자에 대해서는 제약 사항이 몇 가지 더 있습니다.
  • auto를 사용하면 형식이 아닌 템플릿 파라미터에서도 일반적인 형식의 값을 선언할 수 있습니다.