std::is_same vs std::is_base_of in C++
In modern C++, compile-time type introspection is primarily handled
by the <type_traits> header, where
std::is_same and std::is_base_of are two
fundamental tools for evaluating relationships between types. While both
return a compile-time boolean value, they serve fundamentally different
purposes: std::is_same checks whether two types are
strictly identical, whereas std::is_base_of tests for an
inheritance hierarchy between class types. This article breaks down how
each trait works, their key technical differences, edge cases, and
appropriate use cases.
Understanding std::is_same
std::is_same<T, U> checks for exact type
equivalence at compile time. It yields true if
T and U refer to the exact same type, and
false otherwise.
Crucially, std::is_same is sensitive to type qualifiers,
references, and distinct types even if they are structurally
compatible:
- Const and Volatile:
std::is_same<int, const int>::valueisfalse. - References:
std::is_same<int, int&>::valueisfalse. - Inheritance:
std::is_same<Base, Derived>::valueis alwaysfalse. - Type Aliases:
using MyInt = int;followed bystd::is_same<int, MyInt>::valueistrue, because aliases do not create distinct types.
Understanding std::is_base_of
std::is_base_of<Base, Derived> determines whether
Base is a base class of Derived. It yields
true if:
Baseis an unambiguous and accessible or inaccessible base ofDerived.- Both
BaseandDerivedare the same non-union class type (regardless of cv-qualifiers).
If either type is a primitive, a union, or a non-class type,
std::is_base_of evaluates to false. Top-level
const and volatile qualifiers are ignored when
comparing types with std::is_base_of.
Core Differences
| Feature | std::is_same<A, B> |
std::is_base_of<A, B> |
|---|---|---|
| Relationship Tested | Exact type identity | Inheritance hierarchy (is-a relationship) |
| Base and Derived Classes | false |
true |
| Identical Class Types | true |
true (if class type) |
| Identical Primitive Types | true (e.g., int,
int) |
false (e.g.,
int, int) |
cv-qualifiers
(const/volatile) |
Strict (int \(\neq\) const int) |
Ignored on class types |
| Private/Protected Inheritance | N/A | true (evaluates relationship
regardless of access) |
Practical Code Comparison
#include <iostream>
#include <type_traits>
struct Base {};
struct Derived : Base {};
int main() {
// 1. Inheritance verification
std::cout << std::is_same<Base, Derived>::value; // Outputs 0 (false)
std::cout << std::is_base_of<Base, Derived>::value; // Outputs 1 (true)
// 2. Exact same class
std::cout << std::is_same<Base, Base>::value; // Outputs 1 (true)
std::cout << std::is_base_of<Base, Base>::value; // Outputs 1 (true)
// 3. Modifiers (const)
std::cout << std::is_same<Base, const Base>::value; // Outputs 0 (false)
std::cout << std::is_base_of<Base, const Base>::value; // Outputs 1 (true)
// 4. Fundamental types
std::cout << std::is_same<int, int>::value; // Outputs 1 (true)
std::cout << std::is_base_of<int, int>::value; // Outputs 0 (false)
}When to Use Which
- Use
std::is_samewhen implementing template specializations, verifying template argument matches, or writing concepts that demand exact type conformity. It ensures that no implicit conversions, polymorphism, or unintended base-class slicing occur. - Use
std::is_base_ofwhen constraining generic algorithms to an object-oriented class hierarchy. It is commonly used in SFINAE (std::enable_if) or C++20 concepts to accept any object belonging to an inheritance tree, enabling compile-time polymorphism.