I have seen boost::call_traits used many times in templates I’ve been using, not quite understanding what they were doing. They are also the first metaprogramming concept I got to read about, so it was a bit exciting. Turns out they are a lot simpler than I expected, and they stroke me as absolute necessary building blocks for any solid templates.
The call_traits basically wraps the templated type (T) and produce a set of ideal & optimized types to be used as return values, parameters passing, etc. That is, the call_traits will detect the type T and what best types to use in different circumstances.
The other great advantage of using call_traits to wrap your type T is to prevent the “references to references” problem. For instance, if one of your template is generated from a reference type, and your template contains a reference to the base type, then you end up with a reference to a reference. Thankfully, call_traits detect such issues and act accordingly (and automatically).
call_traits generate four templated types to be used safely in various circumstance:
boost::call_traits<T>::param_type boost::call_traits<T>::reference boost::call_traits<T>::const_reference boost::call_traits<T>::value_type
The param_type trait is the ideal type for T to be passed as parameter. If T = “int” for instance, the param_type will be “int const” (basically allowing the passing as value, considering int is a fundamental type). But for a large-ish class like say “myClass”, the the param_type is going to be “const myClass&”, effectively passing the class as reference.
The reference and const_reference are similar to param_type, but ensure that T is passed as reference, either with or without const.
The value_type is mostly useful for the return type value of a method. Most of the time value_type is the same as T, but in some cases like for pure arrays, value_type will be a pointer. Because value_type can be different from T, it seems like it IS still useful in some circumstances to use T as is, but I’m not completely sure when.
The documentation gives a great series of example on how a type T become in each of its four traits. It seems pretty important to keep that help handy.
From now on, I will always use a block like so at the top of my templates, for uses in my members definitions:
typedef typename boost::call_traits<T>::param_type param_type; typedef typename boost::call_traits<T>::reference reference; typedef typename boost::call_traits<T>::const_reference const_reference; typedef T value_type; typedef typename boost::call_traits<T>::value_type result_type;