std:: common_type
|
Definido en el encabezado
<type_traits>
|
||
|
template
<
class
...
T
>
struct common_type ; |
(desde C++11) | |
Determina el tipo común entre todos los tipos
T...
, es decir, un tipo al que todos los
T...
pueden convertirse explícitamente. Si existe tal tipo (determinado según las reglas siguientes), el miembro
type
nombra ese tipo. De lo contrario, no existe el miembro
type
.
-
Si
sizeof...
(
T
)
es cero, no existe el miembro
type. -
Si
sizeof...
(
T
)
es uno (es decir,
T...contiene solo un tipoT0), el miembrotypedenomina el mismo tipo que std :: common_type < T0, T0 > :: type si existe; de lo contrario no existe el 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 diferente, el miembrotypedenota el mismo tipo que std :: common_type < std:: decay < T1 > :: type , std:: decay < T2 > :: type > :: type , si existe; si no, no hay miembrotype; - De lo contrario, si existe una especialización de usuario para std :: common_type < T1, T2 > , se utiliza 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
typedenota ese tipo, consulte el operador condicional ;
-
Si aplicar
std::decay
a al menos uno de
|
(desde C++20) |
-
-
De lo contrario, no hay ningún miembro
type.
-
De lo contrario, no hay ningún 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 miembrotypedenota std :: common_type < typename std :: common_type < T1, T2 > :: type , R... > :: type si tal tipo existe. En todos los demás casos, no existe el miembrotype.
Si cualquier tipo en el paquete de parámetros
T
no es un tipo completo, (posiblemente calificado cv)
void
, o un array de límite desconocido, el comportamiento es indefinido.
Si una instanciación de una plantilla anterior depende, directa o indirectamente, de un tipo incompleto, y esa instanciación podría producir un resultado diferente si ese tipo se completara hipotéticamente, el comportamiento no está definido.
Contenidos |
Tipos anidados
| Nombre | Definición |
type
|
el tipo común para todos
T
|
Tipos auxiliares
|
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 identidad para ambos
T1yT2.
Si tal especialización tiene un miembro llamado
type
, debe ser un miembro público y no ambiguo que nombre un tipo no calificado cv y no referencia al cual tanto
T1
como
T2
sean 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 añade
common_type
especializaciones en violación de estas reglas tiene comportamiento indefinido.
Tenga en cuenta que el comportamiento de un programa que añade una especialización a cualquier otra plantilla
(excepto para
std::basic_common_reference
)
(since C++20)
de
<type_traits>
es indefinido.
Las siguientes especializaciones ya están proporcionadas por la biblioteca estándar:
|
especializa el rasgo
std::common_type
(especialización de plantilla de clase) |
|
|
especializa el rasgo
std::common_type
(especialización de plantilla de clase) |
|
|
(C++23)
|
determina el tipo común de dos
pair
s
(especialización de plantilla de clase) |
|
(C++23)
|
determina el tipo común de un
tuple
y un tipo
tuple-like
(especialización de plantilla de clase) |
determina el tipo común de un iterador y un tipo
basic_const_iterator
adaptado
(especialización de plantilla de clase) |
Implementación posible
// plantilla primaria (usada para cero tipos) template<class...> struct common_type {}; // un tipo template<class T> struct common_type<T> : common_type<T, T> {}; namespace detail { template<class...> using void_t = void; template<class T1, class T2> using conditional_result_t = decltype(false ? std::declval<T1>() : std::declval<T2>()); template<class, class, class = void> struct decay_conditional_result {}; template<class T1, class T2> struct decay_conditional_result<T1, T2, void_t<conditional_result_t<T1, T2>>> : std::decay<conditional_result_t<T1, T2>> {}; template<class T1, class T2, class = void> struct common_type_2_impl : decay_conditional_result<const T1&, const T2&> {}; // implementación de C++11: // template<class, class, class = void> // struct common_type_2_impl {}; template<class T1, class T2> struct common_type_2_impl<T1, T2, void_t<conditional_result_t<T1, T2>>> : decay_conditional_result<T1, T2> {}; } // dos tipos template<class T1, class T2> struct common_type<T1, T2> : std::conditional<std::is_same<T1, typename std::decay<T1>::type>::value && std::is_same<T2, typename std::decay<T2>::type>::value, detail::common_type_2_impl<T1, T2>, common_type<typename std::decay<T1>::type, typename std::decay<T2>::type>>::type {}; // 3+ tipos namespace detail { 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...> : detail::common_type_multi_impl<void, T1, T2, R...> {}; |
Notas
Para tipos aritméticos no sujetos a promoción, el tipo común puede verse como el tipo de la expresión aritmética (posiblemente de modo mixto) como T0 ( ) + T1 ( ) + ... + Tn ( ) .
Ejemplos
Demuestra aritmética de modo mixto en una clase definida por el programa:
#include <iostream> #include <type_traits> template<class T> struct Number { T n; }; template<class T, class U> constexpr Number<std::common_type_t<T, U>> operator+(const Number<T>& lhs, const Number<U>& rhs) { return {lhs.n + rhs.n}; } void describe(const char* expr, const Number<int>& x) { std::cout << expr << " is Number<int>{" << x.n << "}\n"; } void describe(const char* expr, const Number<double>& x) { std::cout << expr << " is Number<double>{" << x.n << "}\n"; } int main() { Number<int> i1 = {1}, i2 = {2}; Number<double> d1 = {2.3}, d2 = {3.5}; describe("i1 + i2", i1 + i2); describe("i1 + d2", i1 + d2); describe("d1 + i2", d1 + i2); describe("d1 + d2", d1 + d2); }
Salida:
i1 + i2 is Number<int>{3}
i1 + d2 is Number<double>{4.5}
d1 + i2 is Number<double>{4.3}
d1 + d2 is Number<double>{5.8}
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Aplicado a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| LWG 2141 | C++11 | el tipo de resultado del operador condicional no estaba decaído | se decae el tipo de resultado |
| LWG 2408 | C++11 |
common_type
no era compatible con SFINAE
|
hecho compatible con SFINAE |
| LWG 2460 | C++11 |
common_type
las especializaciones eran casi imposibles de escribir
|
reducido el número de
especializaciones necesarias |
Véase también
|
(C++20)
|
especifica que dos tipos comparten un tipo común
(concept) |