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 miembrotype. - Si
sizeof...(T)es uno (es decir,T...contiene solo un tipoT0), el miembrotypedenomina el mismo tipo questd::common_type<T0, T0>::typesi existe; de lo contrario, no hay un miembrotype. - Si
sizeof...(T)es dos (es decir,T...contiene exactamente dos tiposT1yT2),
- Si aplicar std::decay a al menos uno de
T1yT2produce un tipo distinto, el miembrotypedenomina el mismo tipo questd::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>())>::typees un tipo válido, el miembrotypedenota 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 sistd::common_type<T1, T2>::typeexiste, el miembrotypedenotastd::common_type<std::common_type<T1, T2>::type, R...>::typesi 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.
Tipos miembro
| Nombre | Definición |
type
|
El tipo común para toda T...
|
Tipos auxiliares
<tbody> </tbody> template< class... T > using common_type_t = typename common_type<T...>::type; |
(desde C++14) | |
Especializaciones
Los usuarios pueden especializar common_type para los tipos T1 y T2 si
- Al menos uno de
T1yT2depende de un tipo definido por el usuario, y - std::decay es una transformación de identidad tanto para
T1como 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) |
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...> {};
|
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().
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
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 |