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 type T
. 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 expression
true? declval<T0>(): declval<T1>()
is the most specialized common type of T0
and T1
. 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.
The third version uses the conditional operator to determine the common type. Its rules are described at quite a length in section 5.16 of the standard, so I'm not sure I should copy them here.
Simply put, the expression:
boolean-expression ? second-operand : third-operand
has a "common type" of the second and third operands, if such exists. The decltype
specifier is then used to "convert" the expression into a type-specifier.
Long Story Short: The decltype is making the C++ compiler determine the closest ancestor type for it.
Tertiary Operator's have the resulting static type of the closest ancestor of the two possible expressions.
E.g:
A inherits from B
X inherits from Y inherits from B
<expression> ? <expression with static type A> : <expression with static type X>
= <expression with static type B> // this is how the C++ parser sees it
This is how the C++ language works. The decltype just makes the typedef be the static type of the result of that expression (whatever type the C++ compiler determines it to be)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With