std::common_type vs std::conditional in C++
This article explains the core distinctions between
std::common_type and std::conditional, two
foundational type traits provided in the C++
<type_traits> header. While both utilities select a
type at compile time, they serve entirely different purposes:
std::conditional acts as a compile-time
if/else branch based on a boolean value,
whereas std::common_type deduces a single common type to
which all listed types can be implicitly converted.
What is
std::conditional?
std::conditional is essentially a ternary operator
(?:) evaluated at compile time for types. It takes three
template arguments: a bool condition, a true-case type, and
a false-case type.
- Syntax:
std::conditional<bool Condition, class T, class F>::type(orstd::conditional_t<Condition, T, F>in C++14 and later). - Behavior: If
Conditionevaluates totrue, the resulting type isT. Iffalse, the resulting type isF.
Example:
#include <type_traits>
#include <cstdint>
// Selects uint64_t if 64-bit systems are targeted, otherwise uint32_t
using PlatformInt = std::conditional_t<(sizeof(void*) == 8), std::uint64_t, std::uint32_t>;std::conditional does not perform any deduction,
conversion, or compatibility checks between T and
F. It strictly branches based on the boolean condition.
What is
std::common_type?
std::common_type computes a shared type to which all
passed types can be implicitly converted. It accepts a parameter pack of
types rather than a boolean flag.
- Syntax:
std::common_type<class... Types>::type(orstd::common_type_t<Types...>in C++14 and later). - Behavior: It determines the result type of a
ternary expression involving instances of each type:
decltype(true ? std::declval<T1>() : std::declval<T2>()). If no implicit conversion path exists between the types,std::common_typeresults in a substitution failure (SFINAE).
Example:
#include <type_traits>
// Deduces double because int implicitly converts to double
using ResultType = std::common_type_t<int, double>; // ResultType is double
// Works with multiple types
using WideType = std::common_type_t<short, int, float, double>; // WideType is doublestd::common_type is also extensible; you can specialize
it for user-defined types (e.g., custom numeric or dimensional types) to
define how they interoperate.
Key Distinctions
| Feature | std::conditional |
std::common_type |
|---|---|---|
| Primary Role | Type selection based on a boolean expression. | Type deduction based on implicit convertibility. |
| Input Parameters | Exactly one bool condition
and two types. |
Variadic (one or more types). |
| Relationship of Types | Types can be completely unrelated. | Types must be mutually convertible to a common target. |
| Custom Specialization | Not intended to be specialized. | Can be specialized for user-defined types. |
When to Use Which
- Use
std::conditionalwhen you need to toggle an implementation detail or data type based on a static compile-time check (e.g., selecting fixed-width types depending on platform architecture, or picking an allocator strategy based on a template policy). - Use
std::common_typewhen writing generic functions or classes that accept mixed types and need to deduce a safe return type (e.g., heterogeneous arithmetic operations likeoperator+, or determining the element type for a collection constructed from mixed inputs).