Step 4 of 9
Writing your own trait
The standard traits aren't magic. A trait is just a template struct with a value member, and specializations give different answers for different types. You can write your own in a few lines, exactly the way the standard library does.
#include <iostream>
#include <map>
#include <string>
#include <type_traits>
template <typename T>
struct is_map : std::false_type {}; // general case: no
template <typename K, typename V, typename C, typename A>
struct is_map<std::map<K, V, C, A>> : std::true_type {}; // any std::map: yes
template <typename T>
inline constexpr bool is_map_v = is_map<T>::value;
template <typename T>
std::string show(const T& x) {
if constexpr (is_map_v<T>) {
std::string out = "{";
for (const auto& [k, v] : x) out += k + ":" + std::to_string(v) + " ";
return out + "}";
} else {
return std::to_string(x);
}
}
int main() {
std::map<std::string, int> ages = {{"ann", 31}, {"bo", 4}};
std::cout << show(ages) << "\n" << show(42) << "\n";
std::cout << is_map_v<int> << is_map_v<std::map<int, int>> << "\n";
}
{ann:31 bo:4 }
42
01
How it works
std::false_typeandstd::true_typeare tiny structs withstatic constexpr bool value = false(ortrue). Inheriting from them gives your trait itsvalue.- The second definition is a partial specialization: it's still a template (over the key, value, comparator and allocator types), but it only matches
std::map<...>. The compiler picks it whenever the type is a map, and the general version otherwise. is_map_vis the usual_vshortcut, a variable template that reads::valuefor you.std::mapreally has four template parameters. The last two have defaults, which is why you normally write only two. A specialization must list all of them to match every possible map.
Your task
std::vector has two template parameters, the element type and the allocator, so your specialization is over std::vector<T, A>. Then size_of uses if constexpr (is_vector_v<T>) to call x.size() only when x is a vector.
Your turn: write the is_vector trait and is_vector_v, then use it in size_of(const T& x): for a vector, return its size(); for anything else, return 1.
Previous: Type traits Next: Constraining templates with concepts