2012-09-05 15 views
7

Para ver cómo funcionaba, miré la implementación libstdC++ de std::common_type en el encabezado type_traits. Tengo que admitir que realmente no entiendo cómo funciona. Aquí está:std :: common_type implementation

/// 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; 
    }; 

Entiendo bien cómo funcionan la primera, segunda y cuarta declaraciones. Sin embargo, no logro entender cómo funciona la tercera declaración. ¿Podría alguien tratar de explicar el mecanismo utilizado aquí?

Respuesta

8

En primer lugar, std::declval<T>() produce un valor r de tipo T. Intentar hacer cualquier cosa con el valor fallará, por lo que solo se puede usar en un contexto no evaluado. A continuación, el operador ternario deduce su tipo como el tipo más especializado común a ambos argumentos (si no existe tal tipo, falla). Por lo tanto, el tipo de la expresión

true? declval<T0>(): declval<T1>() 

es el tipo más común especializado de T0 y T1. Todo lo que queda es convertir esta expresión en un tipo y asegurarse de que no se evalúe. decltype(expr) hace justamente esto. Claramente, la versión de dos argumentos de la carne de la lógica: los otros están ahí para tratar el caso de esquina (un argumento) y para aprovechar la versión de dos argumentos para producir el tipo común de tipos arbitrarios.

3

La tercera versión usa el operador condicional para determinar el tipo común. Sus reglas se describen con bastante detalle en la sección 5.16 de la norma, por lo que no estoy seguro de que deba copiarlas aquí.

En pocas palabras, la expresión:

boolean-expression ? second-operand : third-operand 

tiene un "tipo común" de la segunda y tercera operandos, en caso de existir. El especificador decltype se usa para "convertir" la expresión en un especificador de tipo.

+3

+1, una buena lectura sobre * magic * incluyendo el operador ternario: [Conditional Love: FOREACH redux] (http://www.artima.com/cppsource/foreach.html) –

+0

Ok, creo que finalmente obtuve el truco. Por lo tanto, se basa en el hecho de que el operador ternario deduce sus tipos de retorno en función de sus operandos. No pensó en eso. ¡Gracias! – Morwenn

1

Relato breve: El decltype hace que el compilador C++ determine el tipo de ancestro más cercano para él.

Operadores terciarios tienen el tipo estático resultante del ancestro más cercano de las dos expresiones posibles.

por ejemplo:

Un hereda de B

X hereda de Y hereda de 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 

Esto es cómo funciona el lenguaje C++. El decltype simplemente hace que typedef sea el tipo estático del resultado de esa expresión (cualquiera sea el tipo que el compilador de C++ determine)

+0

Esto es un poco engañoso en el uso de los términos * ancestro * y en el ejemplo que usa herencia. Claro que hace eso, pero hace mucho más, si uno de los tipos es * convertible * al otro, elegirá el segundo tipo, por ejemplo '(verdadero? 1, 1.)' tiene el tipo 'doble' como' int' es convertible a double, aunque no hay * antecesor * o * inheritance *. –

Cuestiones relacionadas