Skip to content
C++

Type Traits Quick Reference

C++ type traits cheatsheet — primary type categories, type relationships, modifications, composite traits, and common patterns.

Primary Type Categories

cpp
#include <type_traits>

// is_X<T>::value or is_X_v<T> (C++17)
std::is_void_v<void>             // true
std::is_integral_v<int>          // true: int, bool, char, short, long...
std::is_floating_point_v<float>  // true: float, double, long double
std::is_array_v<int[5]>          // true
std::is_pointer_v<int*>          // true
std::is_reference_v<int&>        // true: lvalue or rvalue reference
std::is_lvalue_reference_v<int&> // true
std::is_rvalue_reference_v<int&&>// true
std::is_function_v<int(int)>     // true (function types, not pointers)
std::is_enum_v<Color>            // true
std::is_class_v<std::string>     // true (struct and class)
std::is_union_v<U>               // true (unions)
std::is_null_pointer_v<nullptr_t>// true

Composite Type Categories

cpp
std::is_arithmetic_v<int>         // true: integral || floating_point
std::is_fundamental_v<int>        // true: arithmetic || void || nullptr_t
std::is_scalar_v<int*>            // true: arithmetic || pointer || enum || nullptr_t
std::is_object_v<int>             // true: not function, reference, or void
std::is_compound_v<std::string>   // true: not fundamental
std::is_member_pointer_v<int S::*>// true: member pointer types

Type Properties

cpp
// const/volatile
std::is_const_v<const int>        // true
std::is_volatile_v<volatile int>  // true

// Special member functions
std::is_default_constructible_v<T>
std::is_copy_constructible_v<T>
std::is_move_constructible_v<T>
std::is_copy_assignable_v<T>
std::is_move_assignable_v<T>
std::is_destructible_v<T>

// Nothrow variants
std::is_nothrow_move_constructible_v<T>
std::is_nothrow_destructible_v<T>

// Triviality (important for memcpy eligibility)
std::is_trivially_copyable_v<T>       // safe to memcpy
std::is_trivially_destructible_v<T>   // dtor does nothing
std::is_trivial_v<T>                  // trivially default constructible + trivially copyable
std::is_standard_layout_v<T>          // predictable layout (no virtual, single access)
std::is_pod_v<T>                      // trivial + standard layout (deprecated C++20)

// Misc
std::is_empty_v<T>                    // no data members, no virtual
std::is_abstract_v<T>                 // has pure virtual
std::is_polymorphic_v<T>              // has any virtual
std::is_aggregate_v<T>                // C++17: aggregate init eligible
std::is_signed_v<int>                 // true
std::is_unsigned_v<unsigned>          // true

Type Relationships

cpp
std::is_same_v<int, int>              // true
std::is_same_v<int, long>             // false (even if same size)

std::is_base_of_v<Base, Derived>      // true
std::is_convertible_v<int, double>    // true (implicit conversion exists)
std::is_nothrow_convertible_v<T, U>   // C++20

// C++20
std::is_pointer_interconvertible_with_class_v<Struct, Member>
std::is_corresponding_member_v<S1, S2, Member1, Member2>

Type Modifications

cpp
// Add/remove const-volatile
std::remove_const_t<const int>          // int
std::remove_volatile_t<volatile int>    // int
std::remove_cv_t<const volatile int>    // int
std::add_const_t<int>                   // const int
std::add_volatile_t<int>                // volatile int

// References
std::remove_reference_t<int&>           // int
std::remove_reference_t<int&&>          // int
std::add_lvalue_reference_t<int>        // int&
std::add_rvalue_reference_t<int>        // int&&

// Pointers
std::remove_pointer_t<int*>             // int
std::add_pointer_t<int>                 // int*

// Arrays
std::remove_extent_t<int[5]>            // int
std::remove_all_extents_t<int[3][4]>    // int
std::rank_v<int[3][4]>                  // 2
std::extent_v<int[3][4], 0>             // 3
std::extent_v<int[3][4], 1>             // 4

// Combine cv + ref removal
std::remove_cvref_t<const int&>         // int (C++20)
std::decay_t<const int&>                // int (also decays arrays/functions to pointers)

Utility Traits

cpp
// Conditional type selection
std::conditional_t<true,  int, double>  // int
std::conditional_t<false, int, double>  // double

// Enable/disable overloads (pre-C++20)
template<typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
void f(T x);

// Identity
std::type_identity_t<int>              // int (prevents deduction)

// Make signed/unsigned
std::make_signed_t<unsigned int>       // int
std::make_unsigned_t<int>              // unsigned int

// Common type (result of ternary operator)
std::common_type_t<int, double>        // double
std::common_type_t<int, long>          // long

// Underlying enum type
std::underlying_type_t<Color>          // e.g. int

SFINAE and Concepts Patterns

cpp
// C++17 SFINAE: enable_if
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
safe_divide(T a, T b) { return b ? a/b : 0; }

// C++17: if constexpr (cleaner)
template<typename T>
auto process(T x) {
    if constexpr (std::is_integral_v<T>) return x * 2;
    else return x + 1.0;
}

// C++20: concepts (best)
template<std::integral T>
T safe_divide(T a, T b) { return b ? a/b : 0; }

// void_t for detecting member existence (C++17)
template<typename, typename = void>
struct has_size : std::false_type {};
template<typename T>
struct has_size<T, std::void_t<decltype(std::declval<T>().size())>>
    : std::true_type {};

// C++20 requires (better)
template<typename T>
concept HasSize = requires(T t) { t.size(); };

Common Patterns Summary

cpp
// "Is T a numeric type?"
template<typename T>
constexpr bool is_numeric = std::is_arithmetic_v<T> && !std::is_same_v<T, bool>;

// "Strip all qualifiers from T"
template<typename T>
using bare_t = std::remove_cvref_t<T>;

// "Is T a container (has begin/end)?"
template<typename T, typename = void>
inline constexpr bool is_range = false;
template<typename T>
inline constexpr bool is_range<T, std::void_t<
    decltype(std::begin(std::declval<T&>())),
    decltype(std::end(std::declval<T&>()))
>> = true;
Edit on GitHub