Flaming Dangerzone

Even more type traits

I have before talked about some type traits I use often. I feel like there are more type manipulations that I want to share.

Metafunction invocation

One of my first posts here was about handling dependent names. I showed how one can use alias templates to avoid having to type typename ...::type all over. I actually went one step further and use a small alias to define those aliases.

template <typename T>
using Invoke = typename T::type;

template <typename T>
using UnderlyingType = Invoke<std::underlying_type<T>>;

I find that more convenient to both write and read even though it does very little.

Unqualified types

I have talked about types stripped of qualifiers before. At the time I decided to call those "bare types". After trying the name on some people and after looking around for ideas, I have a found a much better and much more obvious name. I now simply call these unqualified types. It is clear what I means, and I no longer need to explain what "bare" means.

template <typename T>
using Unqualified = RemoveCv<RemoveReference<T>>;

Raw storage

It does not happen often, but I have before needed to use raw storage of appropriate size and alignment for some type (boost::optional without move semantics, I am looking at you).

The C++11 standard library provides us with std::aligned_storage for this task. It provides storage with a certain size and alignment.

However, I find the interfaces of this trait somewhat annoying. It is annoying to use sizeof and alignof manually when all you want is "storage appropriate for some T".

More than once have I used the type std::aligned_storage<sizeof(T), alignof(T)> instead of std::aligned_storage<sizeof(T), alignof(T)>::type by mistake (I even made that mistake in a previous post of mine). I have also seen other people making that mistake on Stack Overflow.

So, obviously, I wrote a template alias to ease this.

template <typename T>
using StorageFor = Invoke<std::aligned_storage<sizeof(T), alignof(T)>>;

I will expand on this trait latter when I explore the use cases and show improvements to it.

Improved decay

The standard library provides the std::decay trait to simulate pass-by-value semantics. This is a transformation that shares similarities with the Unqualified trait, but there are important differences: std::decay will transform array and function types into pointer types, just like when passing them by value.

std::make_tuple, for example, performs this kind of transformation. Using Unqualified would not work for something like the following:

void f();
auto tuple = std::make_tuple(0, f);

One cannot have members of function types. Decaying that type to a function pointer does the natural thing and allows that to work. The result in the example has type std::tuple<int, void(*)()>.

But std::make_tuple performs a slightly more involved transformation: in order to allow creating tuples with references, one can use std::reference_wrapper (through std::ref and std::cref).

int x = 42;
auto tuple = std::make_tuple(0, std::ref(x));

This creates a std::tuple<int, int&> not a std::tuple<int, std::reference_wrapper<int>>. std::decay cannot do that.

This kind of transformation is used in other places in the standard library as well (std::bind for example), and comes up pretty often when writing generic tools. So I think it's worth having a trait for it. I actually used it in the tuple series to implement make_tuple.

template <typename T>
struct unwrap_reference
: identity<T> {};

template <typename T>
struct unwrap_reference<std::reference_wrapper<T>>
: identity<T&> {};

template <typename T>
struct decay_reference : unwrap_reference<Decay<T>> {};
template <typename T>
using DecayReference = Invoke<decay_reference<T>>;