More type traits
The C++11 standard library provides us with several type traits, many of which need compiler magic and thus cannot be reproduced within the language. These type traits are very useful, but, at least for me, they still don’t cover a lot of common uses. This article will cover some useful traits that are not found in the standard library.
The first one is the simplest one. An identity metafunction is one that returns its argument unchanged. It’s the most basic type transformation: it performs no transformation at all. The implementation is rather simple as well.
This metafunction already exists in the Boost.MPL library, but since it is so simple, I don’t mind writing it myself.
But, what is a “do-nothing” construct useful for?
Well, it is useful when we need to do something, but want to do nothing! An
example is when we use
std::conditional and the branches are evaluated lazily.
If one of the branches is a known type instead of a type computation, we need to
make a no-op type computation out of it.
We can define an identity alias that “invokes”
identity by accessing its
Is this one as useful as the other one? Why not simply write the type directly
instead of wrapping it in
There is one useful property of this alias: since dependent names cannot take
part in type deduction, we can use this alias to prevent type deduction. This
isn’t very common, but it does pop up sometimes. In the standard library we have
std::forward as an example of a function where type deduction is not desired.
Since this is the only meaningful use case for this alias, I prefer to give it a
more appropriate name:
And what if we wrote this alias directly without invoking
This is truly mostly useless. I only know two use cases for it, and they’re both shady.
We can use it to create a temporary array, but who would want that?
We can also use it to work around the quirks of the C declarator syntax:
And this is already too much talking about constructs that do nothing.
static_assert is a new language feature that allows us to produce errors when
a certain compile-time boolean expression is false. One use case of this feature
is providing custom error messages when using templates. Take for example,
std::result_of. Could it be implemented as follows?
It could not. The problem with this is that the static assert will always
trigger, even if the primary template is not instantiated. To prevent it from
triggering we need to make it use a dependent name. I use something like
std::false_type, but with a type parameter. Or better, a variadic parameter
pack, for when we’re writing variadic templates.
With it the primary template can be made to work now.
This can also be used for writing SFINAE-based traits, but I’m leaving that for another article.
Conditions and logical meta-ops
Several of the standard library traits provide a boolean
value member. And
std::enable_if take a boolean as a parameter.
std::enable_if is a special case, so I will leave it for a future article.
One could write a simple
Conditional alias as follows.
More often than not, the boolean parameter will be the
value member of some
type trait. In order to reduce boilerplate, I prefer to have
simply take a type trait and automatically access its
This is dandy if the condition is a simple type trait. But when the condition
is, for example, a conjunction of two traits, it does not work very well. Using
the first definition of
Conditional, code would look like this:
With the second definition, we can’t write this. Unless we make metafunctions that work on boolean traits just like the logical operators do for boolean values!
Making the and and or meta-operations binary would again make it hard to read when there are more than two conditions involved, so let us make them variadic templates.
Sometimes I need to strip a type of any references and cv-qualifiers, usually to compare it to a known non-reference, non-cv type. I call these bare types†.
Transforming a type into a bare type can be achieved with a combination of
RemoveCv, but I prefer to have a specialized trait
anyway, especially since the two must be combined in a specific order:
RemoveCv< RemoveReference< int const& > > is
RemoveReference< RemoveCv< int const& > > is
int const. Having a specific
trait avoids this potential mistake.
† I have since found a better name for these.
Reference and cv-qualifier propagators
The standard library has
and similar traits for
volatile and references, that test, add, or remove
those qualifications from a type. I find it lacks one other type of operation
with those qualifications: copying the qualifications from one type to another.
Implementing this for cv-qualifiers is quite trivial: use the testing trait, and
if it yields true, use the adding trait.
Implementing them for the value category (i.e. object, lvalue reference, rvalue reference) is a bit more involved, but nothing too complicated: two nested conditionals.
See the follow-up article for even more traits.