std:: common_type
|
Определено в заголовочном файле
<type_traits>
|
||
|
template
<
class
...
T
>
struct common_type ; |
(начиная с C++11) | |
Определяет общий тип среди всех типов
T...
, то есть тип, к которому все
T...
могут быть явно преобразованы. Если такой тип существует (определяется согласно правилам ниже), член
type
называет этот тип. В противном случае, члена
type
не существует.
-
Если
sizeof...
(
T
)
равен нулю, член
typeотсутствует. -
Если
sizeof...
(
T
)
равен единице (т.е.
T...содержит только один типT0), членtypeобозначает тот же тип, что и std :: common_type < T0, T0 > :: type , если он существует; в противном случае членtypeотсутствует. -
Если
sizeof...
(
T
)
равен двум (т.е.
T...содержит ровно два типаT1иT2),
-
-
Если применение
std::decay
хотя бы к одному из типов
T1иT2даёт другой тип, то членtypeобозначает тот же тип, что и std :: common_type < std:: decay < T1 > :: type , std:: decay < T2 > :: type > :: type , если такой тип существует; в противном случае членtypeотсутствует; - Иначе, если существует пользовательская специализация для std :: common_type < T1, T2 > , используется эта специализация;
-
Иначе, если
std::
decay
<
decltype
(
false
?
std::
declval
<
T1
>
(
)
:
std::
declval
<
T2
>
(
)
)
>
::
type
является допустимым типом, то член
typeобозначает этот тип, см. условный оператор ;
-
Если применение
std::decay
хотя бы к одному из типов
|
(начиная с C++20) |
-
-
В противном случае, член
typeотсутствует.
-
В противном случае, член
-
Если
sizeof...
(
T
)
больше двух (т.е.,
T...состоит из типовT1, T2, R...), то если std :: common_type < T1, T2 > :: type существует, членtypeобозначает std :: common_type < typename std :: common_type < T1, T2 > :: type , R... > :: type , если такой тип существует. Во всех остальных случаях членаtypeне существует.
Если любой тип в пакете параметров
T
не является полным типом, (возможно, cv-квалифицированным)
void
, или массивом неизвестной границы, поведение не определено.
Если создание экземпляра шаблона выше зависит, прямо или косвенно, от неполного типа, и это создание экземпляра может дать другой результат, если бы этот тип был гипотетически завершен, поведение не определено.
Содержание |
Вложенные типы
| Название | Определение |
type
|
общий тип для всех
T
|
Вспомогательные типы
|
template
<
class
...
T
>
using common_type_t = typename common_type < T... > :: type ; |
(начиная с C++14) | |
Специализации
Пользователи могут специализировать
common_type
для типов
T1
и
T2
если
-
По крайней мере один из
T1иT2зависит от пользовательского типа, и -
std::decay
является тождественным преобразованием для обоих
T1иT2.
Если такая специализация имеет член с именем
type
, он должен быть публичным и однозначным членом, называющим тип без cv-квалификаторов и не являющийся ссылкой, к которому явно преобразуемы как
T1
, так и
T2
. Кроме того,
std
::
common_type
<
T1, T2
>
::
type
и
std
::
common_type
<
T2, T1
>
::
type
должны обозначать один и тот же тип.
Программа, которая добавляет специализации
common_type
в нарушение этих правил, имеет неопределённое поведение.
Обратите внимание, что поведение программы, которая добавляет специализацию для любого другого шаблона
(кроме
std::basic_common_reference
)
(since C++20)
из
<type_traits>
является неопределённым.
Следующие специализации уже предоставлены стандартной библиотекой:
|
специализирует
std::common_type
трейт
(специализация шаблона класса) |
|
|
специализирует
std::common_type
трейт
(специализация шаблона класса) |
|
|
(C++23)
|
определяет общий тип двух
pair
объектов
(специализация шаблона класса) |
|
(C++23)
|
определяет общий тип
tuple
и
tuple-like
типа
(специализация шаблона класса) |
определяет общий тип итератора и адаптированного
basic_const_iterator
типа
(специализация шаблона класса) |
Возможная реализация
// первичный шаблон (используется для нуля типов) template<class...> struct common_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: // 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> {}; } // два типа 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+ типов 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...> {}; |
Примечания
Для арифметических типов, не подвергающихся продвижению, общий тип можно рассматривать как тип (возможно, смешанного режима) арифметического выражения, такого как T0 ( ) + T1 ( ) + ... + Tn ( ) .
Примеры
Демонстрирует смешанную арифметику для пользовательского класса:
#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); }
Вывод:
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}
Отчёты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Исправленное поведение |
|---|---|---|---|
| LWG 2141 | C++11 | тип результата условного оператора не подвергался decay | тип результата подвергается decay |
| LWG 2408 | C++11 |
common_type
не был SFINAE-friendly
|
сделан SFINAE-friendly |
| LWG 2460 | C++11 |
common_type
специализации было практически невозможно написать
|
уменьшено количество
необходимых специализаций |
Смотрите также
|
(C++20)
|
определяет, что два типа имеют общий тип
(концепт) |