Flaming Dangerzone

Handling dependent names

Annoying typename

Let's consider the example of converting an enumerator to the corresponding value of the enumeration underlying type. In the old C++ we would always have such a conversion made implicitly, but now there is another choice. Scoped enumerations, informally known as enum classes, do not have such an implicit conversion. The conversion is still possible, but it must be done explicitly.

enum class foo : int { bar = 42 };

int main() {
    int underlying = static_cast<int>(foo::bar);
    assert(underlying == 42);
}

Doing this requires us to know what the underlying type is. But if we are writing generic code that works with various enums, we do not have that knowledge. The standard library header provides us with the type transformation std::underlying_type for this purpose. With it one can write a generic function that works for any enum and does not require knowing the underlying type.

#include 
#include 

template <typename Enum>
std::underlying_type<Enum>::type to_underlying(Enum enum) {
    return static_cast<std::underlying_type<Enum>::type>(enum);
}

enum class foo : int { bar = 42 };

int main() {
    int underlying = to_underlying(foo::bar);
    assert(underlying == 42);
}

But if we try to compile this... it does not compile. Ooops. Clang produces the following error:

foo.cpp:3:1: fatal error: missing 'typename' prior to dependent type name 'std::underlying_type::type'
std::underlying_type::type to_underlying(Enum enum) {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typename 
1 error generated.

The code is missing the typename disambiguator. C++ needs that so the compiler knows that the dependent names we used are type names.

template <typename Enum>
typename std::underlying_type<Enum>::type to_underlying(Enum enum) {
    return static_cast<typename std::underlying_type<Enum>::type>(enum);
}

With this the function compiles, and works fine. In this example, the need for typename is just a minor nuissance. But in a more complex scenario, it can be quite annoying. Imagine if the Enum type parameter was foo& instead. Perhaps it was not deduced from a parameter, but obtained as the result of some qux_trait type transformation. Before underlying_type could be applied to it, it would be necessary to remove the reference from it. The entire type computation would look something like this:

typename std::underlying_type<
    typename std::remove_reference<
        typename qux_trait<T>::type
    >::type
>::type

On one line that is very hard to read. All those typename keywords and ::type accesses add a lot of clutter. Until C++11, this was something one had to put up with.

Lovely alias templates

C++11 brings us alias templates, sometimes referred to as typedef templates. These are basically typedefs that can have template parameters. The syntax for declaring an alias template looks like this:

template <typename T>
using type_erased_unique_ptr = std::unique_ptr<T, std::function<void(T*)>>;

I noticed two interesting properties of alias templates: by itself, an usage of an alias template is not a dependent name; and an alias template can refer to a dependent name. By combining these two, we have an alternative solution to the problem that the typename disambiguator solves. As an example, consider the following alias template:

template <typename T>
using better_underlying_type = typename std::underlying_type<T>::type;

With this, we can simply write better_underlying_type, and since it is not a dependent name, we don't need to prefix it with typename. By applying the same idea to other traits, the triply nested example from above can be rewritten as follows:

better_underlying_type< better_remove_reference< better_qux_trait< T > > >

That looks almost readable now. We just need to get better names for our "better traits". Ideally, we would like to keep the same names. Those from the standard library we will have to keep in our own namespace anyway, so they won't conflict with the ones from std. But what about our own traits like qux_trait?

One could move the original qux_trait to a detail namespace and name the alias qux_trait. But in fact, it is not that good an option because these new "better" traits do no completely replace the old style ones.

There are cases when one doesn't actually want to access the type member. Perhaps doing so would trigger infinite recursion as in the following example:

template <typename T>
struct foo {
    using type = std::conditional<
        std::is_something<T>::value,
        something<T>,
        // if we accessed ::type here,
        // it would result in infinite recursion
        foo<typename change<T>::type>
    >::type::type; // so we only access ::type
                   // on the result of std::conditional
}

In such situations one needs the "lazy" metafunction that doesn't actually get "invoked" until we access ::type on it. So we actually need to leave the traditional metafunction around.

How to name the two styles of traits is obviously a subjective style matter. One can add a _lazy prefix to the old ones, or put them in a lazy namespace. Or one can use a casing convention for ones and another for the others.

Me, I settled upon the convention that PascalCase traits are aliases that stand for the result of the corresponding metafunction, while snake_case traits are the actual metafunctions that need ::type to be "invoked". That's the style I will be using in this series. With my style, our nested metafunction example looks like this:

UnderlyingType< RemoveReference< QuxTrait< T > > >