std::common_type
| Definiert in der Kopfdatei <type_traits> |
||
| template< class... T > struct common_type; |
(seit C++11) | |
Ermittelt den gemeinsamen Typ aller Typen T..., d.h. einen Typ, zu dem alle T... explizit konvertiert werden können. Wenn ein solcher Typ existiert (wie nach den folgenden Regeln bestimmt), bezeichnet das Element type diesen Typ. Andernfalls gibt es kein Element type.
- Wenn sizeof...(T) null ist, gibt es kein Element
type. - Wenn sizeof...(T) eins ist (d. h.
T...enthält nur einen TypT0), bezeichnet das Elementtypedenselben Typ wie std::common_type<T0, T0>::type, falls dieser existiert; andernfalls gibt es kein Elementtype. - Wenn sizeof...(T) zwei ist (d. h.
T...enthält genau zwei TypenT1undT2),
- Wenn die Anwendung von std::decay auf mindestens einen der Typen
T1undT2einen anderen Typ ergibt, bezeichnet das Elementtypedenselben Typ wie std::common_type<std::decay<T1>::type, std::decay<T2>::type>::type, falls dieser existiert; wenn nicht, gibt es kein Elementtype; - Andernfalls, wenn eine Benutzerspezialisierung für std::common_type<T1, T2> existiert, wird diese Spezialisierung verwendet;
- Andernfalls, wenn std::decay<decltype(false ? std::declval<T1>() : std::declval<T2>())>::type ein gültiger Typ ist, bezeichnet das Element
typediesen Typ, siehe den bedingten Operator;
- Wenn die Anwendung von std::decay auf mindestens einen der Typen
|
(seit C++20) |
- Andernfalls gibt es kein Element
type.
- Andernfalls gibt es kein Element
- Wenn sizeof...(T) größer als zwei ist (d. h.
T...besteht aus den TypenT1, T2, R...), dann, wenn std::common_type<T1, T2>::type existiert, bezeichnet das Elementtypestd::common_type<typename std::common_type<T1, T2>::type, R...>::type, wenn ein solcher Typ existiert. In allen anderen Fällen gibt es kein Elementtype.
Wenn ein Typ im Parameterpaket T kein vollständiger Typ ist (möglicherweise cv-qualifiziert) void oder ein Array unbekannter Bindung, ist das Verhalten undefiniert.
Wenn eine Instanziierung einer Vorlage davon direkt oder indirekt von einem unvollständigen Typ abhängt und diese Instanziierung ein anderes Ergebnis liefern könnte, wenn dieser Typ hypothetisch vervollständigt würde, ist das Verhalten undefiniert.
Inhalt |
[bearbeiten] Verschachtelte Typen
| Name | Definition |
type
|
der gemeinsame Typ für alle T |
[bearbeiten] Hilfstypen
| template< class... T > using common_type_t = typename common_type<T...>::type; |
(seit C++14) | |
[bearbeiten] Spezialisierungen
Benutzer können common_type für die Typen T1 und T2 spezialisieren, wenn
- Mindestens einer der Typen
T1undT2von einem benutzerspezifizierten Typ abhängt, und - std::decay eine Identitätsoperation für sowohl
T1als auchT2ist.
Wenn eine solche Spezialisierung ein Element namens type hat, muss dieses ein öffentliches und eindeutiges Element sein, das einen cv-unqualifizierten Nicht-Referenz-Typ bezeichnet, zu dem sowohl T1 als auch T2 explizit konvertierbar sind. Zusätzlich müssen std::common_type<T1, T2>::type und std::common_type<T2, T1>::type denselben Typ bezeichnen.
Ein Programm, das common_type-Spezialisierungen unter Verstoß gegen diese Regeln hinzufügt, hat undefiniertes Verhalten.
Beachten Sie, dass das Verhalten eines Programms, das eine Spezialisierung zu einer anderen Vorlage hinzufügt (außer für std::basic_common_reference)(seit C++20) aus <type_traits>, undefiniert ist.
Die folgenden Spezialisierungen werden bereits von der Standardbibliothek bereitgestellt
| spezialisiert den std::common_type Trait (Klassentemplate-Spezialisierung) | |
| spezialisiert den std::common_type Trait (Klassentemplate-Spezialisierung) | |
| (C++23) |
ermittelt den gemeinsamen Typ von zwei pairs(Klassenvorlagenspezialisierung) |
| (C++23) |
ermittelt den gemeinsamen Typ eines tuple und eines tuple-like Typs(class template specialization) |
ermittelt den gemeinsamen Typ eines Iterators und eines angepassten basic_const_iterator-Typs(Klassenvorlagenspezialisierung) |
[bearbeiten] Mögliche Implementierung
// primary template (used for zero types) template<class...> struct common_type {}; // one 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&> {}; // C++11 implementation: // 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> {}; } // two 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...> {}; |
[bearbeiten] Hinweise
Für arithmetische Typen, die keiner Promotion unterliegen, kann der gemeinsame Typ als der Typ des (möglicherweise gemischten) arithmetischen Ausdrucks wie T0() + T1() + ... + Tn() betrachtet werden.
[bearbeiten] Beispiele
Demonstriert gemischte Arithmetik auf einer vom Programm definierten Klasse
#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); }
Ausgabe
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}[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| LWG 2141 | C++11 | Der Ergebnistyp des bedingten Operators wurde nicht decay-verarbeitet | decay-verarbeitete den Ergebnistyp |
| LWG 2408 | C++11 | common_type war nicht SFINAE-freundlich |
Wurde SFINAE-freundlich gemacht. |
| LWG 2460 | C++11 | common_type-Spezialisierungen waren fast unmöglich zu schreiben |
reduzierte die Anzahl der benötigten Spezialisierungen |
[bearbeiten] Siehe auch
| (C++20) |
gibt an, dass zwei Typen einen gemeinsamen Typ haben (Konzept) |