std::common_type
Definido en el archivo de encabezado <type_traits>
|
||
template< class... T > struct common_type; |
(desde C++11) | |
Determina el tipo común entre todos los tipos T...
, que es el tipo al que todos los tipos T...
pueden ser convertidos implícitamente. Si tal tipo existe (se determina de acuerdo a las reglas posteriores), el tipo miembro type
denomina ese tipo. De lo contrario, no hay un miembro type
.
- Si sizeof...(T) es cero, no hay un miembro
type
. - Si sizeof...(T) es uno (es decir,
T...
contiene solo un tipoT0
), el miembrotype
denomina el mismo tipo que std::common_type<T0, T0>::type si existe; de lo contrario, no hay un miembrotype
. - Si sizeof...(T) es dos (es decir,
T...
contiene exactamente dos tiposT1
yT2
),
- Si aplicar std::decay a al menos uno de
T1
yT2
produce un tipo distinto, el miembrotype
denomina el mismo tipo que std::common_type<std::decay<T1>::type, std::decay<T2>::type>::type, si existe; si no, no hay un miembrotype
. - De lo contrario, si hay una especialización de usuario para std::common_type<T1, T2>, se usa esa especialización;
- De lo contrario, si std::decay<decltype(false ? std::declval<T1>() : std::declval<T2>())>::type es un tipo válido, el miembro
type
denota ese tipo;
- Si aplicar std::decay a al menos uno de
|
(desde C++20) |
- De lo contrario, no hay un miembro
type
.
- De lo contrario, no hay un miembro
- Si sizeof...(T) es mayor que dos (es decir,
T...
consiste en los tiposT1, T2, R...
), entonces si std::common_type<T1, T2>::type existe, el miembrotype
denota std::common_type<std::common_type<T1, T2>::type, R...>::type si tal tipo existe. En todos los demás casos, no hay un miembrotype
.
Los tipos en el paquete de parámetros T
deberá cada uno ser un tipo completo, (posiblemente calificado-cv) void, o un array de límite desconocido. De lo contrario, el comportamiento está indefinido.
Si la instanciación de una plantilla anterior depende, directa o indirectamente, de un tipo incompleto, y esa instanciación podría generar un resultado distinto si ese tipo hipotéticamente se completara, el comportamiento está indefinido.
Contenido |
[editar] Tipos miembro
Nombre | Definición |
type
|
El tipo común para toda T...
|
[editar] Tipos auxiliares
template< class... T > using common_type_t = typename common_type<T...>::type; |
(desde C++14) | |
[editar] Especializaciones
Los usuarios pueden especializar common_type
para los tipos T1
y T2
si
- Al menos uno de
T1
yT2
depende de un tipo definido por el usuario, y - std::decay es una transformación de identidad tanto para
T1
como paraT2
.
Si tal especialización tiene un miembro denominado type
, debe ser un tipo miembro público e inequívoco que denomina un tipo que no es una referencia sin calificadores-cv al cual tanto T1
como T2
son explícitamente convertibles. Adicionalmente, std::common_type<T1, T2>::type y std::common_type<T2, T1>::type deben denotar el mismo tipo.
Un programa que agrega especializaciones de common_type
en violación de estas reglas tiene un comportamiento indefinido.
Observa que el comportamiento de un programa que agrega una especialización a cualquier otra plantilla de <type_traits>
está indefinido.
La biblioteca estándar ya proporciona las siguientes especializaciones:
Especializa el rasgo std::common_type. (especialización de plantilla de clase) | |
Especializa el rasgo std::common_type. (especialización de plantilla de clase) |
[editar] Posible implementación
template <typename...> using void_t = void; // plantilla primaria (usada para cero tipos) template <class...> struct common_type {}; //////// un tipo template <class T> struct common_type<T> : common_type<T, T> {}; //////// dos tipos template <class T1, class T2> using cond_t = decltype(false ? std::declval<T1>() : std::declval<T2>()); template <class T1, class T2, class=void> struct common_type_2_impl {}; template <class T1, class T2> struct common_type_2_impl<T1, T2, void_t<cond_t<T1, T2>>> { using type = typename std::decay<cond_t<T1, T2>>::type; }; template <class T1, class T2> struct common_type<T1, T2> : common_type_2_impl<typename std::decay<T1>::type, typename std::decay<T2>::type> {}; //////// tres o más tipos template <class AlwaysVoid, class T1, class T2, class...R> struct common_type_multi_impl {}; template <class T1, class T2, class...R> struct common_type_multi_impl< void_t<typename common_type<T1, T2>::type>, T1, T2, R...> : common_type<typename common_type<T1, T2>::type, R...> {}; template <class T1, class T2, class... R> struct common_type<T1, T2, R...> : common_type_multi_impl<void, T1, T2, R...> {}; |
[editar] Notas
Para los tipos aritméticos que no están sujetos a promoción, el tipo común puede verse como el tipo de la expresión aritmética (posiblemente de modo mixto) tal que T0() + T1() + ... + Tn().
[editar] Ejemplo
Demuestra aritmética de modo mixto en una clase definida por el usuario
#include <iostream> #include <type_traits> template <class T> struct Number { T n; }; template <class T, class U> Number<typename std::common_type<T, U>::type> operator+(const Number<T>& lhs, const Number<U>& rhs) { return {lhs.n + rhs.n}; } int main() { Number<int> i1 = {1}, i2 = {2}; Number<double> d1 = {2.3}, d2 = {3.5}; std::cout << "i1i2: " << (i1 + i2).n << "\ni1d2: " << (i1 + d2).n << '\n' << "d1i2: " << (d1 + i2).n << "\nd1d2: " << (d1 + d2).n << '\n'; }
Salida:
i1i2: 3 i1d2: 4.5 d1i2: 4.3 d1d2: 5.8
[editar] Informes de defectos
Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.
ID | Aplicado a | Comportamiento según lo publicado | Comportamiento correcto |
---|---|---|---|
LWG 2141 | C++11 | common_type<int, int>::type es int&& | Tipo del resultado decae |
LWG 2408 | C++11 | common_type no es amigable con SFINAE
|
Se hizo amigable con SFINAE |
LWG 2460 | C++11 | Es casi imposible escribir las especializaciones para common_type
|
Se necesita un número reducido de especializaciones |