c++ templates c++11 types metaprogramming

c++ - Es `std:: common_type` asociativo?



templates c++11 (3)

¡No es asociativo! Aquí hay un programa donde falla:

#include <type_traits> struct Z; struct X { X(Z); }; // enables conversion from Z to X struct Y { Y(X); }; // enables conversion from X to Y struct Z { Z(Y); }; // enables conversion from Y to Z using namespace std; static_assert( is_same<common_type< X, common_type<Y,Z>::type >::type, common_type< common_type<X,Y>::type, Z >::type>::value, "std::common_type is not associative." );

La idea es simple: el siguiente diagrama muestra lo que common_type calcula:

X,Y -> Y Y,Z -> Z X,Z -> X

La primera línea es lógica, ya que X se puede convertir a Y , pero no al revés. Lo mismo para las otras dos líneas. Una vez que X e Y se combinan y se combinan con Z , obtenemos Z Por otro lado, la combinación de Y y Z y la combinación de X con el resultado da X Por lo tanto los resultados son diferentes.

La razón fundamental para que esto sea posible es que la convertibilidad no es transitiva, es decir, si X es convertible a Y y Y convertible a Z , no se sigue que X sea ​​convertible a Z Si la convertibilidad fuera transitiva, entonces las conversiones funcionarán en ambos sentidos y, por lo tanto, el common_type no se podría calcular de manera inequívoca y dar lugar a un error de tiempo de compilación.

Este razonamiento es independiente de la versión estándar. Se aplica a C ++ 11, C ++ 14 y el próximo C ++ 17.

La clase de plantilla std::common_type calcula un tipo común a una lista de tipos variadic. Se define utilizando el tipo de retorno del operador ternario x:y?z recursivamente. A partir de esa definición, no es obvio para mí, si el cálculo de un std::common_type<X,Y> es asociativo, es decir, si

using namespace std; static_assert( is_same<common_type< X, common_type<Y,Z>::type >::type, common_type< common_type<X,Y>::type, Z >::type>::value, "" );

nunca lanzará un error en tiempo de compilación para todos los tipos X , Y y Z para los cuales la is_same<...> es válida.

Tenga en cuenta, que no estoy preguntando si

static_assert( is_same<common_type<X,Y>::type, common_type<Y,X>::type>::value, "" );

siempre va a disparar. Obviamente no lo será. Lo anterior es una pregunta completamente diferente.

Tenga en cuenta también que la especificación de std::common_type cambió ligeramente en C ++ 14 y probablemente volverá a cambiar en C ++ 17. Así que las respuestas pueden ser diferentes para diferentes versiones de la norma.


Esto falla en MinGW-w64 (gcc 4.9.1). También falla en VS2013 y (gracias Baum mit Augen) en gcc5.2 o clang 3.7 con libc ++.

#include <type_traits> using namespace std; struct Z; struct X{operator Z();}; struct Y{operator X();}; struct Z{operator Y();}; static_assert( is_same<common_type<X,Y>::type, common_type<Y,X>::type>::value, "" ); // PASS static_assert( is_same<common_type<X,Z>::type, common_type<Z,X>::type>::value, "" ); // PASS static_assert( is_same<common_type<Y,Z>::type, common_type<Z,Y>::type>::value, "" ); // PASS static_assert( is_same<common_type< X, common_type<Y,Z>::type >::type, common_type< common_type<X,Y>::type, Z >::type>::value, "" ); // FAIL...


#include <type_traits> struct T2; struct T1 { T1(){} T1(int){} operator T2(); }; struct T2 { operator int() { return 0; } }; struct T3 { operator int() { return 0; } }; T1::operator T2() { return T2(); } using namespace std; using X = T1; using Y = T2; using Z = T3; int main() { true?T2():T3(); // int static_assert(std::is_same<std::common_type_t<T2, T3>, int>::value, "Not int"); true?T1():(true?T2():T3()); // T1 static_assert(std::is_same<std::common_type_t<T1, std::common_type_t<T2, T3>>, T1>::value, "Not T1"); // ----------------------------------------- true?T1():T2(); // T2 static_assert(std::is_same<std::common_type_t<T1, T2>, T2>::value, "Not T2"); true?(true?T1():T2()):T3(); // int static_assert(std::is_same<std::common_type_t<std::common_type_t<T1, T2>, T3>, int>::value, "Not int"); // ----------------------------------------- static_assert( is_same<common_type_t< X, common_type_t<Y,Z> >, common_type_t< common_type_t<X,Y>, Z > >::value, "Don''t match"); }

¡Ay! La gimnasia mental aquí me dolió la cabeza, pero se me ocurrió un caso que no se compilaba, imprimiendo "No coinciden", con gcc 4.9.2 y con "C ++ 14" (gcc 5.1) en ideone . Ahora si eso es conforme o no es un asunto diferente ...

Ahora la reclamación es para tipos de clase, std::common_type_t<X, Y> debe ser X o Y , pero he obligado a std::common_type_t<T2, T3> a convertir a int .

Por favor, intente con otros compiladores y hágame saber qué pasa!