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!