6 분 소요

개요

C++11 부터 STL 에서는 템플릿 메타 프로그래밍을 위해 다양한 타입 특성들을 제공합니다.

예를 들면 다음과 같이 템플릿 인자로 전달된 T가 포인터 타입인지 아닌지에 따라 코딩할 수 있습니다.

또한 is_pointer<T>::value와 같이 열거형 조회를 하는 것은 is_pointer_v<T>로 인스턴스화 된 버전을 제공하여 코딩을 조금 더 간편하게 합니다.

1
2
3
4
5
6
7
8
9
template<typename T>
void Func(T t) { 
    if (std::is_pointer<T>::value) { // std::is_pointer_v<T> 와 동일
        // T가 포인터 타입인 경우
    } 
    else {
        // T가 포인터 타입이 아닌 경우
    }
}

Helper

항목 내용
integral_constant(C++11~) 정수형 타입을 생성합니다.
true_type(C++11~) integral_constant<bool, true>의 별칭입니다.
false_type(C++11~) integral_constant<bool, false>의 별칭입니다.
bool_constant (C++17~) integral_constant<bool, true 나 false>
1
2
3
4
5
typedef std::integral_constant<int, 2> TwoType;
typedef std::integral_constant<int, 3> ThreeType;

TwoType two;
ThreeType three;

기본 타입 카테고리

항목 내용
is_void (C++11~) void 타입인지 검사합니다.
is_integral (C++11~) 정수형 타입인지 검사합니다.
is_floating_point (C++11~) 실수형 타입인지 검사합니다.
is_array (C++11~) 배열 타입인지 검사합니다.
is_enum (C++11~) 열거형인지 검사합니다.
is_union (C++11~) 공용체 타입인지 검사합니다.
is_class (C++11~) 구조체, 클래스 타입인지 검사합니다.
is_function (C++11~) 함수 타입인지 검사합니다.
is_pointer (C++11~) 포인터 타입인지 검사합니다.
is_lvalue_reference (C++11~) 좌측값 참조 타입인지 검사합니다.
is_rvalue_reference (C++11~) 우측값 참조 타입인지 검사합니다.
is_member_object_pointer (C++11~) 개체 멤버 변수의 포인터 타입인지 검사합니다.
is_member_function_pointer (C++11~) 개체 멤버 함수의 포인터 타입인지 검사합니다.
is_null_pointer (C++14~) nullptr_t 타입인지 검사합니다.

복합 타입 카테고리

복합 타입 카테고리는 기본 타입들을 조합하여 검사합니다.

항목 내용
is_arithmetic (C++11~) 정수, 실수 타입인지 검사합니다.
is_fundamental (C++11~) 정수, 실수, void, nullptr_t 타입인지 검사합니다.
is_scalar (C++11~) 스칼라 타입인지 검사합니다.
is_object (C++11~) 좌측값과 포인터 타입인지 검사합니다.
is_reference (C++11~) 참조 타입(좌측값 참조우측값 참조)인지 검사합니다.
is_compound (C++11~) 배열, 함수, 개체 포인터, 함수 포인터, 멤버 변수 포인터, 멤버 함수 포인터, 참조자, 구조체/클래스, 공용체, 열거형인지 검사합니다. int와 같은 기본 타입은 false 입니다.
is_member_pointer (C++11~) 멤버 변수 포인터, 멤버 함수 포인터 인지 검사합니다.

타입 특성

항목 내용  
is_const (C++11~) const 타입인지 검사합니다.  
is_volatile (C++11~) volatile 타입인지 검사합니다.  
is_trivial (C++11~) Trivial 타입 인지 검사합니다.  
is_trivially_copyable (C++11~) 스칼라 타입 이나 Trivial 타입 처럼 간단하게 복사 가능한지 검사합니다.  
is_standard_layout (C++11~) 표준 레이아웃 타입 인지 검사합니다.  
is_pod(C++11~C++20) POD 타입 인지 검사합니다.  
is_literal_type(C++11~C++17) 리터럴 타입 인지 검사합니다.  
is_empty (C++11~) 멤버 변수, 가상 함수, 비어있지 않은 부모 개체가 없는 빈 개체인지 검사합니다.  
is_polymorphic (C++11~) 가상 함수가 있거나, 가상 함수를 상속한 다형성 타입인지 검사합니다.  
is_abstract (C++11~) 적어도 1개 이상의 순가상 함수가 있는 추상 클래스 타입인지 검사합니다.  
is_signed (C++11~) 양수/음수 처리를 지원하는 타입인지 검사합니다.  
is_unsigned (C++11~) 양수/음수 처리를 지원하는 않는 타입인지 검사합니다.  
is_final (C++14~) final 클래스 타입인지 검사합니다.  
has_unique_object_representations (C++17~) (작성중)  
is_aggregate (C++17~) (작성중)  
is_bounded_array (C++20~) 요소의 갯수가 알려진 배열 타입인지 검사합니다.  
is_unbounded_array (C++20~) 요소의 갯수가 알려지지 않은 배열 타입인지 검사합니다.  
is_scoped_enum (C++23~) (작성중)  
is_implicit_lifetime (C++23~) (작성중)  

다음은 사용예입니다.

1

복사/이동/대입/소멸 속성

항목 내용
is_constructible (C++11~)
is_trivially_constructible (C++11~)
is_nothrow_constructible (C++11~)
열거된 인자 타입으로 생성될 수 있는지 검사합니다.
is_default_constructible (C++11~)
is_trivially_default_constructible (C++11~)
is_nothrow_default_constructible (C++11~)
기본 생성자로 생성될 수 있는지 검사합니다.
is_copy_constructible (C++11~)
is_trivially_copy_constructible (C++11~)
is_nothrow_copy_constructible (C++11~)
복사 생성자로 생성될 수 있는지 검사합니다.
is_move_constructible (C++11~)
is_trivially_move_constructible (C++11~)
is_nothrow_move_constructible (C++11~)
이동 생성자로 생성될 수 있는지 검사합니다.
is_assignable (C++11~)
is_trivially_assignable (C++11~)
is_nothrow_assignable (C++11~)
주어진 인자 타입으로 대입될 수 있는지 검사합니다.
is_copy_assignable (C++11~)
is_trivially_copy_assignable (C++11~)
is_nothrow_copy_assignable (C++11~)
복사 대입될 수 있는지 검사합니다.
is_move_assignable (C++11~)
is_trivially_move_assignable (C++11~)
is_nothrow_move_assignable (C++11~)
이동 대입될 수 있는지 검사합니다.
is_destructible (C++11~)
is_trivially_destructible (C++11~)
is_nothrow_destructible (C++11~)
소멸 가능한 타입인지 검사합니다.
has_vitual_destuctor (C++11~) 가상 소멸자인지 검사합니다.
is_swappable_with (C++17~)
is_swappable (C++17~)
is_nothrow_swappable_with (C++17~)
is_nothrow_swappable (C++17~)
(작성중)

속성 질의

항목 내용
alignment_of (C++11~) 메모리 정렬에 필요한 크기를 구합니다.
alignof() 와 동일하며, C++11 이전 컴파일러와 호환성을 유지합니다.
rank (C++11~) 다차원 배열의 차원수를 구합니다.
extent (C++11~) 배열 차원의 요소 수를 구합니다.

타입 관계

항목 내용
is_same (C++11~) 두개의 타입이 동일한지 검사합니다. 이때 constvolatile을 고려합니다. 즉 intconst int는 다른 타입입니다.
is_base_of (C++11~) BaseDerived의 부모인지 검사합니다.(CloneTraits 구현IsDerivedFrom 참고)
is_convertible (C++11~)
is_nothrow_convertible (C++20~)
From 타입이 To로 변환 가능한지 검사합니다.
is_invocable (C++17~)
is_invocable_r (C++17~)
is_nothrow_invocable (C++17~)
is_nothrow_invocable_r (C++17~)
(작성중)
is_layout_compatible (C++20~) 표준 레이아웃 타입 등 메모리 레이아웃이 호환되는지 검사합니다.
is_pointer_interconvertible_base_of (C++20~) (작성중)

const-volatile

항목 내용
remove_cv (C++11~)
remove_const (C++11~)
remove_volatile (C++11~)
constvolatile 한정자를 제거합니다.
add_cv (C++11~)
add_const (C++11~)
add_volatile (C++11~)
constvolatile 한정자를 추가합니다.

참조자

항목 내용
remove_reference (C++11~) 타입에서 좌측값 참조, 우측값 참조참조성을 제거합니다.
add_lvalue_reference (C++11~)
add_rvalue_reference (C++11~)
타입에서 좌측값 참조, 우측값 참조참조성을 추가합니다.

포인터

항목 내용
remove_pointer (C++11~) 타입에서 포인터성을 제거합니다.
add_pointer (C++11~) 타입에서 포인터성을 추가합니다.

부호 수정

항목 내용
make_signed (C++11~) 양수/음수 처리를 지원하는 타입으로 변경합니다.
make_unsigned (C++11~) 양수/음수 처리를 지원하지 않는 타입으로 변경합니다.

배열

항목 내용
remove_extent (C++11~) 배열의 첫번째 차원을 제거합니다. 즉, int[][][]int[][]가 됩니다.
remove_all_extents (C++11~) 배열의 모든 차원을 제거합니다.

기타 변환

항목 내용
aligned_storage(C++11~C++23) (작성중)
aligned_union(C++11~C++23) (작성중)
decay (C++11~) 참조성, const, volatile등의 속성을 떼고, 타입으로 만듭니다. 즉 const int&이던 int&&이던 모두 int가 됩니다. 또한 배열은 포인터로 만듭니다.
이와는 반대로 참조성을 붙여서 인자를 전달하는 것은 ref(), cref()를 참고하세요.
enable_if (C++11~) 지정한 조건이 참인 경우만 템플릿을 활성화 합니다.
conditional (C++11~) (작성중)
common_type (C++11~) (작성중)
underlying_type (C++11~) (작성중)
result_of(C++11~C++20)
invoke_result (C++17~)
(작성중)
void_t (C++17~) (작성중)
type_identity (C++17~) (작성중)
remove_cvref (C++20~) const, volatile, 좌측값 참조, 우측값 참조를 제거한 타입을 구합니다.
common_reference (C++20~)
basic_common_reference (C++20~)
주어진 타입들의 공통된 참조 형식을 구합니다.

enable_if

주어진 조건이 참인 경우만 템플릿을 활성화 시킵니다. 내부적으로 SFINAE를 이용합니다. 즉, 함수 인자, 리턴 타입, 템플릿 인자등에 사용하여 조건이 참이어서 종속 타입type이 정의되어 있으면 함수 오버로딩 후보군으로 활성화 시키고, 정의되어 있지 않으면 비활성 시킵니다.

1
2
3
4
5
template<bool B, class T = void>
struct enable_if {}; // 조건이 거짓이면 아무 정의 안합니다.
 
template<class T>
struct enable_if<true, T> { typedef T type; }; // 조건이 참이면 `T`와 동일한 종속 타입 `type`을 만듭니다.

예를 들어 다음은 int타입이나 double타입이나 심지어 string까지 operator +()가 정의되었다면 모두 실행 가능한데요,

1
2
3
4
5
6
7
template<typename T> 
T Add(T a, T b) {
    return a + b;
}

EXPECT_TRUE(Add(10, 20) == 30); // 정수 합
EXPECT_TRUE(Add(std::string("Hello"), std::string("World")) == std::string("HelloWorld")); 

다음처럼 정수 타입만 실행되게 수정할 수 있습니다.

  1. typename U = enable_if<조건>::type으로 조건이 거짓이면 SFINAE에 의해 함수 오버로딩 후보 목록에서 제외되게 합니다.(이때 U는 사용되지 않으므로, typename = enable_if<조건>::type 와 같이 생략하는게 좋습니다.)

  2. std::is_integral<T>::valueT가 정수 타입인지 검사합니다.

1
2
3
4
5
6
7
8
9
10
11
12
template<
    typename T, 
    typename U = typename std::enable_if< // U 는 사용하지 않으므로 생략 가능합니다. typename = typename std::enable_if<
        std::is_integral<T>::value // 조건. 정수형 타입이면 true 입니다.
    >::type // 조건이 true인 경우에만 enable_if<>::type이 정의됩니다.
> 
T Add(T a, T b) {
    return a + b;
}

EXPECT_TRUE(Add(10, 20) == 30); // 정수 합
EXPECT_TRUE(Add(std::string("Hello"), std::string("World")) == std::string("HelloWorld")); // (X) 컴파일 오류. 정수 타입이 아니어서 enable_if<>::type이 정의되지 않고, SFINIE에 의해 오버로딩된 함수 후보 목록에서 제외됩니다. 따라서 함수가 없습니다. 

(C++20~) 컨셉(concept)요구사항(requires)이 추가되어 템플릿 인자auto제약 조건(constraint)을 줄 수 있습니다.

(C++17~) traits 에서의 연산

항목 내용
conjunction (C++17~) (작성중)
disjunction (C++17~) (작성중)
negation (C++17~) (작성중)

(C++20~) 함수

항목 내용
is_pointer_interconvertible_with_class() (C++20~) (작성중)
is_corresponding_member() (C++20~) (작성중)
is_constant_evaluated() (C++20~) 컴파일 타임에 실행중인지, 런타임에 실행중인지 평가합니다.
is_within_lifetime() (C++26~) (작성중)

(C++20~) is_constant_evaluated()

constexpr 함수는 컴파일 타임 함수나 런타임 함수로 동작할 수 있는데요(constexpr 함수 참고),

C++20 부터는 is_constant_evaluated()가 추가되어 constexpr 함수가 컴파일 타임 함수인지 런타임 함수인지 검사할 수 있습니다.

다음 예의 Factorial()함수는 constexpr 함수인데요, is_constant_evaluated()를 이용하여 런타임 함수로 동작할때만 메시지를 출력합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
constexpr int Factorial(int val) {
    
    int result{1}; // 초기화된 지역 변수 정의

    if (val < 1) {
        return 1; // 2개 이상의 리턴문
    }

    for (int i{val}; 0 < i; --i) { // 제어문
        result *= i;
    }
    bool isConstant = std::is_constant_evaluated();

    if (!std::is_constant_evaluated()) {
        std::cout << "Factorial Result : " << result << std::endl; // 컴파일 타임 함수가 아니라면, 메시지를 출력합니다.
    }
    return result;
} 

static_assert(Factorial(5) == 1 * 2 * 3 * 4 * 5); // 컴파일 타임 함수입니다.

int val{5};
int result{Factorial(val)}; // 런타임 함수입니다. 메시지를 출력합니다.
EXPECT_TRUE(result == 1 * 2 * 3 * 4 * 5);

태그:

카테고리:

업데이트:

댓글남기기