std:: common_type
|
Défini dans l'en-tête
<type_traits>
|
||
|
template
<
class
...
T
>
struct common_type ; |
(depuis C++11) | |
Détermine le type commun parmi tous les types
T...
, c'est-à-dire un type vers lequel tous les
T...
peuvent être explicitement convertis. Si un tel type existe (tel que déterminé selon les règles ci-dessous), le membre
type
nomme ce type. Sinon, il n'y a pas de membre
type
.
-
Si
sizeof...
(
T
)
est zéro, il n'y a pas de membre
type. -
Si
sizeof...
(
T
)
est un (c'est-à-dire,
T...contient seulement un typeT0), le membretypedésigne le même type que std :: common_type < T0, T0 > :: type s'il existe ; sinon il n'y a pas de membretype. -
Si
sizeof...
(
T
)
est deux (c'est-à-dire,
T...contient exactement deux typesT1etT2),
-
-
Si l'application de
std::decay
à au moins l'un des types
T1etT2produit un type différent, le membretypedésigne le même type que std :: common_type < std:: decay < T1 > :: type , std:: decay < T2 > :: type > :: type , s'il existe ; sinon, il n'y a pas de membretype; - Sinon, s'il existe une spécialisation utilisateur pour std :: common_type < T1, T2 > , cette spécialisation est utilisée ;
-
Sinon, si
std::
decay
<
decltype
(
false
?
std::
declval
<
T1
>
(
)
:
std::
declval
<
T2
>
(
)
)
>
::
type
est un type valide, le membre
typedésigne ce type, voir l'opérateur conditionnel ;
-
Si l'application de
std::decay
à au moins l'un des types
|
(depuis C++20) |
-
-
Sinon, il n'y a aucun membre
type.
-
Sinon, il n'y a aucun membre
-
Si
sizeof...
(
T
)
est supérieur à deux (c'est-à-dire,
T...consiste en les typesT1, T2, R...), alors si std :: common_type < T1, T2 > :: type existe, le membretypedénote std :: common_type < typename std :: common_type < T1, T2 > :: type , R... > :: type si un tel type existe. Dans tous les autres cas, il n'y a pas de membretype.
Si un type quelconque dans le pack de paramètres
T
n'est pas un type complet, (éventuellement qualifié cv)
void
, ou un tableau de limite inconnue, le comportement est indéfini.
Si l'instanciation d'un template ci-dessus dépend, directement ou indirectement, d'un type incomplet, et que cette instanciation pourrait produire un résultat différent si ce type était hypothétiquement complété, le comportement est indéfini.
Table des matières |
Types imbriqués
| Nom | Définition |
type
|
le type commun pour tous les
T
|
Types auxiliaires
|
template
<
class
...
T
>
using common_type_t = typename common_type < T... > :: type ; |
(depuis C++14) | |
Spécialisations
Les utilisateurs peuvent spécialiser
common_type
pour les types
T1
et
T2
si
-
Au moins l'un des
T1etT2dépend d'un type défini par l'utilisateur, et -
std::decay
est une transformation identité pour
T1etT2.
Si une telle spécialisation possède un membre nommé
type
, celui-ci doit être un membre public et non ambigu qui désigne un type non qualifié cv et non référence vers lequel
T1
et
T2
sont explicitement convertibles. De plus,
std
::
common_type
<
T1, T2
>
::
type
et
std
::
common_type
<
T2, T1
>
::
type
doivent désigner le même type.
Un programme qui ajoute des spécialisations de
common_type
en violation de ces règles a un comportement indéfini.
Notez que le comportement d'un programme qui ajoute une spécialisation à tout autre template
(sauf pour
std::basic_common_reference
)
(depuis C++20)
de
<type_traits>
est indéfini.
Les spécialisations suivantes sont déjà fournies par la bibliothèque standard :
|
spécialise le trait
std::common_type
(spécialisation de modèle de classe) |
|
|
spécialise le trait
std::common_type
(spécialisation de modèle de classe) |
|
|
(C++23)
|
détermine le type commun de deux
pair
s
(spécialisation de modèle de classe) |
|
(C++23)
|
détermine le type commun d'un
tuple
et d'un type
tuple-like
(spécialisation de modèle de classe) |
détermine le type commun d'un itérateur et d'un type
basic_const_iterator
adapté
(spécialisation de modèle de classe) |
Implémentation possible
// modèle primaire (utilisé pour zéro type) template<class...> struct common_type {}; // un type 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&> {}; // implémentation 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> {}; } // deux types 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+ types 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...> {}; |
Notes
Pour les types arithmétiques non soumis à la promotion, le type commun peut être considéré comme le type de l'expression arithmétique (éventuellement en mode mixte) telle que T0 ( ) + T1 ( ) + ... + Tn ( ) .
Exemples
Illustre l'arithmétique en mode mixte sur une classe définie par le programme :
#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); }
Sortie :
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}
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Applicable à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| LWG 2141 | C++11 | le type de résultat de l'opérateur conditionnel n'était pas dégradé | a dégradé le type de résultat |
| LWG 2408 | C++11 |
common_type
n'était pas compatible SFINAE
|
rendu compatible SFINAE |
| LWG 2460 | C++11 |
common_type
les spécialisations étaient quasi impossibles à écrire
|
a réduit le nombre de
spécialisations nécessaires |
Voir aussi
|
(C++20)
|
spécifie que deux types partagent un type commun
(concept) |