Step 3 of 9
Type traits
<type_traits> is a library of compile-time questions and answers about types. It has two kinds of tools:
- Predicates answer yes/no:
std::is_integral_v<T>,std::is_signed_v<T>,std::is_floating_point_v<T>,std::is_pointer_v<T>,std::is_same_v<A, B>. - Transformations produce a new type:
std::remove_cvref_t<T>(stripconstand references),std::make_unsigned_t<T>,std::add_pointer_t<T>.
Combined with if constexpr, a single template can handle each kind of type correctly:
#include <iostream>
#include <string>
#include <type_traits>
template <typename T>
std::string info(const T&) {
std::string s;
if constexpr (std::is_integral_v<T>) {
s = std::is_signed_v<T> ? "signed int" : "unsigned int";
s += " of " + std::to_string(sizeof(T) * 8) + " bits";
} else if constexpr (std::is_floating_point_v<T>) {
s = "floating point";
} else if constexpr (std::is_same_v<T, std::string>) {
s = "std::string";
} else {
s = "something else";
}
return s;
}
int main() {
short a = 1;
unsigned char b = 2;
std::cout << info(a) << "\n" << info(b) << "\n" << info(2.5f) << "\n";
std::cout << info(std::string("x")) << "\n" << info(nullptr) << "\n";
static_assert(std::is_same_v<std::remove_cvref_t<const int&>, int>);
}
signed int of 16 bits
unsigned int of 8 bits
floating point
std::string
something else
Traps
boolandcharare integral types.std::is_integral_v<bool>istrue, so a bool check must come before the general integral check.- A pointer is not integral and not a class, so
std::is_pointer_v<T>needs its own branch. - The parameter is
const T&, butTitself is deduced without theconstand&, sostd::is_same_v<T, std::string>works here. When you're unsure, wrap the type instd::remove_cvref_tfirst. - The last line shows a transformation checked at compile time: stripping
constand&fromconst int&leavesint.
decltype
decltype(expr) is the type of an expression, worked out at compile time without running it. If a is an int and b a double, decltype(a + b) is double. Generic code uses it to name types it can't spell out, like std::vector<decltype(a + b)>, and you can declare a variable with exactly another one's type: decltype(total) copy = 0;.
Your turn: write template <typename T> std::string kind_of(const T&) returning "bool" for bool (careful: bool is an integral type, so check it first), "signed" or "unsigned" for other integral types, "float" for floating point, "pointer" for pointers, and "other" otherwise.