c++template-meta-programmingtype-traitschar-traits

Design considerations for type traits for a set of related types


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.

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.


Solution

  • 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.