The C++ standard library provides std::char_traits<T>
to unify the handling of character data without regard to the specific character type used.
It also provides <type_traits>
for more general metaprogramming.
These two kinds of traits systems have a different different design: std::char_traits<T>
is a class template (with several specializations) that has several static methods. <type_traits>
provides free-standing templates instead of static methods.
Why do these two systems take different approaches?
I'm not seeking opinions. I'm hoping to identify constraints that should be considered when designing a traits system.
<type_traits>
was standardized in C++11; std::char_traits<T>
is older)I'm currently designing version two of a collection of traits for a specific domain. In choosing between a class template with static methods or a collection of standalone templates, I realized I don't know what factors should be considered.
ETA: A specific distinction between the problem spaces, which might go to my third bullet proof above, is that std::char_traits<T>
mostly provides methods of operating on data but <type_traits>
are mostly about operating on types.
Do the different approaches reflect a difference in the problem spaces? (general metaprogramming versus unified handling for a specific finite set of types)
Nowadays, if you want to group a set of related functions without clobbering everybody, you'd probably write those functions as free-standing, and use a namespace
to group them. After all, in the case of class with static
methods, the call site looks like
the_trait_class<some_type>::the_func
and you must have specialized the_trait_class
for some_type
, so you have written each the_func
for each some_type
; in the case of free-standing functions in a namespace, the call site looks like
the_trait_namespace::the_func<some_type>
and you must have specialized each the_func
for each some_type
.
It's fundamentally the same amount of code, with the difference being where <some_type>
is written at the call site, for the same effect.
More on it here.
Is there something you can do in one approach that you cannot do in the other?
Well, you can inherit from a class, so you can think of scenarii where you want to pass some object of class derived from std::char_trait
around; there's even an example on cppreference. But I think the standard could have provided a similar freedom even if char_traits
was implemented via free functions.
Has the language evolved to better suit one approach or the other? (<type_traits> was standardized in C++11; std::char_traits is older)
I think the reason for char_trait
to be implemented the way it is, is mostly historical, and nowadays I'd abandon struct
+static
functions in favour of namespace
+free functions.