Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::common_type implementation

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?

like image 986
Morwenn Avatar asked Sep 05 '12 21:09

Morwenn


3 Answers

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.

like image 72
Dietmar Kühl Avatar answered Nov 15 '22 17:11

Dietmar Kühl


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.

like image 40
eq- Avatar answered Nov 15 '22 18:11

eq-


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)

like image 43
Bob Fincheimer Avatar answered Nov 15 '22 17:11

Bob Fincheimer