Just to see how it worked, I looked at the libstdc++ implementation of std::common_type in the header type_traits. I have to admit that I don’t really understand how it works. Here it is:
/// common_type
template<typename... _Tp>
struct common_type;
template<typename _Tp>
struct common_type<_Tp>
{ typedef _Tp type; };
template<typename _Tp, typename _Up>
struct common_type<_Tp, _Up>
{ typedef decltype(true ? declval<_Tp>() : declval<_Up>()) type; };
template<typename _Tp, typename _Up, typename... _Vp>
struct common_type<_Tp, _Up, _Vp...>
{
typedef typename
common_type<typename common_type<_Tp, _Up>::type, _Vp...>::type type;
};
I understand well how the first, second and fourth declarations work. However, I can’t manage to understand how the third declaration works. Could someone try to explain the mechanism used here?
First off,
std::declval<T>()yields an r-value of typeT. Trying to do anything with the value will fail so it can only be used in an unevaluated context. Next, the ternary operator deduces its type as most specialized type common to both arguments (if there is no such type, it fails). So, the type of the expressionis the most specialized common type of
T0andT1. All what remains is to turn this expression into a type and making sure that it isn’t evaluated.decltype(expr)does just this. Clearly, the two argument version of the beef of the logic: the others are there to deal with the corner case (one argument) and to leverage the two argument version to yield the common type of arbitrary types.