A Laser-Focused Alternative to std::decay Using C++14’s Alias Templates

In the previous blog post, there was some code iterating through a generic container, using a range-for loop like so:

for (const auto& elem : c)

I wanted the C++ compiler to pick a specific traits class template specialization based on the current element’s type. Using just decltype(elem) turned out to be a bug, since the returned type was a const reference to the element’s type, but what I actually wanted was the original element’s type (not a const reference to it).

So, I used std::decay to strip off the “const &” part from the type returned by decltype(elem), leaving only the actual element’s type, such that the compiler could pick the proper traits class specialization based on that type.

However, std::decay performs also additional transformations, like those involving arrays and functions, which aren’t relevant in our case.

A more laser-focused alternative would be using std::remove_reference and std::remove_const (both defined in the <type_traits> standard header).

We could define a simple class template for that purpose:

#include <type_traits> // for remove_const, remove_reference

// Strip off "const &" from "const Type&",
// leaving only "Type".
template <typename T>
class RemoveConstReference
{
private:
  // Strip off the reference part
  typedef typename 
    std::remove_reference<T>::type 
    TypeWithoutRef;

public:

  // Strip off the const,
  // after having stripped off the reference
  typedef typename 
    std::remove_const<TypeWithoutRef>::type 
    type;
};

And then that RemoveConstReference custom-defined template could be applied to strip off “const &” from elem’s type:

typedef 
  RemoveConstReference<decltype(elem)>::type
  ElemType;

And finally the correct traits specialization is picked using code like this:

const int x = 
  Traits<ElemType>::GetSomeProperty(elem);

In addition, C++14 introduced some convenient alias templates. For example: besides C++11’s remove_reference, in C++14 a remove_reference_t alias template was defined:

template <typename T>
using remove_reference_t
  = typename remove_reference<T>::type;

This is basically syntactic sugar, a syntactic shortcut.

Similarly, there’s a C++14 remove_const_t alias template matching C++11’s remove_const.

Using those _t-ending alias templates, it’s possible to further simplify the code to strip off the “const&”, composing remove_const_t and remove_reference_t like this (“std::” prefix omitted for the sake of clarity):

typedef remove_const_t<
  remove_reference_t<
    decltype(elem)>> 
ElemType;

We could even define a custom reusable alias template just for removing “const &”:

template <typename T>
using RemoveConstReference_t = 
  std::remove_const_t<
    std::remove_reference_t<T>>;

And then use it like so:

typedef RemoveConstReference_t<decltype(elem)> 
  ElemType;

const int x = 
  Traits<ElemType>::GetSomeProperty(elem);

These C++14’s alias templates, which are just “syntactic sugar”, are nonetheless convenient to make C++ code simpler and more clear!

 

Leave a Reply

Your email address will not be published. Required fields are marked *