Namespaces
Variants

std:: common_type

From cppreference.net
Metaprogramming library
Type traits
Type categories
(C++11)
(C++11) ( DR* )
Type properties
(C++11)
(C++11)
(C++14)
(C++11) (deprecated in C++26)
(C++11) ( until C++20* )
(C++11) (deprecated in C++20)
(C++11)
Type trait constants
Metafunctions
(C++17)
Supported operations
Relationships and property queries
Type modifications
Type transformations
(C++11) (deprecated in C++23)
(C++11) (deprecated in C++23)
(C++11)
(C++11) ( until C++20* ) (C++17)

common_type
(C++11)
(C++11)
(C++17)
Compile-time rational arithmetic
Compile-time integer sequences
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 tipo T0 ), el miembro type denomina el mismo tipo que std :: common_type < T0, T0 > :: type si existe; de lo contrario no existe el miembro type .
  • Si sizeof... ( T ) es dos (es decir, T... contiene exactamente dos tipos T1 y T2 ),
  • Si aplicar std::decay a al menos uno de T1 y T2 produce un tipo diferente, el miembro type denota el mismo tipo que std :: common_type < std:: decay < T1 > :: type , std:: decay < T2 > :: type > :: type , si existe; si no, no hay miembro type ;
  • 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 type denota ese tipo, consulte el operador condicional ;
(desde C++20)
  • De lo contrario, no hay ningún miembro type .
  • Si sizeof... ( T ) es mayor que dos (es decir, T... consiste en los tipos T1, T2, R... ), entonces si std :: common_type < T1, T2 > :: type existe, el miembro type denota 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 miembro type .

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 T1 y T2 depende de un tipo definido por el usuario, y
  • std::decay es una transformación identidad para ambos T1 y T2 .

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)
determina el tipo común de dos pair s
(especialización de plantilla de clase)
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

especifica que dos tipos comparten un tipo común
(concept)