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!