Understanding std::true_type in C++ Metaprogramming
This article provides an overview of std::true_type in
C++, examining its role as a fundamental building block in compile-time
template metaprogramming. It covers how std::true_type is
defined in the standard library, why it is used as a base class when
creating custom type traits, and how this inheritance simplifies trait
design by providing standard compile-time constants and type
definitions.
What Is std::true_type?
Defined in the <type_traits> header,
std::true_type is a standard type alias for an
instantiation of the template class
std::integral_constant:
namespace std {
using true_type = integral_constant<bool, true>;
}Because it wraps the boolean value true into a distinct
C++ type, it represents a compile-time boolean truth value. It includes
several key members:
value: Astatic constexpr boolmember equal totrue.value_type: A type alias forbool.type: A type alias referring tostd::integral_constant<bool, true>itself.operator bool(): A constexpr conversion operator returningtrue.operator()(since C++14): A constexpr function call operator returningtrue.
Its counterpart is std::false_type, which is an alias
for std::integral_constant<bool, false>.
Using std::true_type as a Base Class
In C++ template metaprogramming, type traits are used to query
characteristics of types at compile time. Instead of manually declaring
static members inside every custom trait, developers inherit from either
std::true_type or std::false_type.
Inheriting from std::true_type automatically equips a
custom struct with:
- The static compile-time constant
value = true. - Standard type aliases (
typeandvalue_type). - Implicit conversion operators that allow instances of the trait to be evaluated as booleans.
This reduces boilerplate and ensures custom traits adhere to the Standard Library convention.
Implementation Pattern
The standard idiom for creating a boolean type trait involves two steps:
- Define a primary template that inherits from
std::false_type. - Define template specializations for types that meet the desired
condition, having them inherit from
std::true_type.
Here is an example that creates a trait to check if a type is a raw pointer:
#include <iostream>
#include <type_traits>
// 1. Primary template: defaults to false
template <typename T>
struct is_pointer_custom : std::false_type {};
// 2. Partial specialization: matches pointer types and inherits true
template <typename T>
struct is_pointer_custom<T*> : std::true_type {};
int main() {
// Access via the inherited ::value member
std::cout << std::boolalpha;
std::cout << is_pointer_custom<int>::value << '\n'; // Output: false
std::cout << is_pointer_custom<int*>::value << '\n'; // Output: true
// Access via inherited implicit conversion
constexpr is_pointer_custom<double*> check;
if (check) {
std::cout << "Type is a pointer\n"; // Output: Type is a pointer
}
}Benefits in Metaprogramming
Using std::true_type as a base class offers several
distinct advantages:
- Standardization: Tools, libraries, and language
features that expect the standard trait interface (such as
std::enable_if,std::void_t, and C++20 concepts) can automatically interact with custom traits. - Tag Dispatching: Because
std::true_typeandstd::false_typeare distinct types, functions can be overloaded based on them, enabling clean compile-time branch selection without runtime overhead. - Consistency: It maintains code uniformity across
C++ codebases by matching the behavior of Standard Library traits like
std::is_integralandstd::is_floating_point.