Spec-Zone.ru › C++

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<decltype(false?std::declval<CR1>():std::declval<CR2>())>::type является допустимым типом, где CR1 и CR2 — это соответственно conststd::remove_reference_t<T1>& и conststd::remove_reference_t<T2>&, член type задаёт этот тип;
(начиная с C++20)
  • В противном случае член type отсутствует.
  • Если sizeof...(T) больше двух (то есть T... состоит из типов T1, T2, R...), то, если существует std::common_type<T1,T2>::type, член type задаёт std::common_type<typenamestd::common_type<T1,T2>::type,R...>::type, если такой тип существует. Во всех остальных случаях член type отсутствует.

Если какой-либо тип в пакете параметров T не является полным типом, типом void (возможно, с квалификаторами cv) или массивом неизвестной границы, поведение не определено.

Если инстанцирование шаблона, приведённого выше, прямо или косвенно зависит от неполного типа и при гипотетическом завершении этого типа результат инстанцирования мог бы измениться, поведение не определено.

Вложенные типы

Имя Определение
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 с нарушением этих правил, имеет неопределённое поведение.

Обратите внимание, что поведение программы, добавляющей специализацию любого другого шаблона из <type_traits>, не определено (за исключением std::basic_common_reference)(начиная с C++20).

Стандартная библиотека уже предоставляет следующие специализации:

std::common_type<std::chrono::duration>
(C++11)
специализирует признак std::common_type
(специализация шаблона класса)
std::common_type<std::chrono::time_point>
(C++11)
специализирует признак std::common_type
(специализация шаблона класса)
std::common_type<std::pair>
(C++23)
определяет общий тип двух pair
(специализация шаблона класса)
std::common_type<tuple-like>
(C++23)
определяет общий тип tuple и типа tuple-like
(специализация шаблона класса)
std::common_type<std::basic_const_iterator>
(C++23)
определяет общий тип итератора и адаптированного типа basic_const_iterator
(специализация шаблона класса)

Возможная реализация

// 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...> {};

Примечания

Для арифметических типов, не подвергаемых повышению, общий тип можно рассматривать как тип арифметического выражения (возможно, смешанного режима), например 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++.

Отчёт об ошибке Применён к Опубликованное поведение Правильное поведение
LWG 2141 C++11 тип результата условного оператора не приводился к типу после decay тип результата приводится к типу после decay
LWG 2408 C++11 common_type не поддерживал SFINAE добавлена поддержка SFINAE
LWG 2460 C++11 common_type было почти невозможно написать уменьшено количество необходимых
специализаций

См. также

common_with
(C++20)
задаёт требование, что два типа имеют общий тип
(концепт)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/types/common_type

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API