Пространства имён
Варианты
Действия

std::common_type

Материал из cppreference.com
< cpp‎ | types
 
 
Библиотека метапрограммирования
Свойства типов
Категории типов
(C++11)
(C++14)  
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Свойства типов
(C++11)
(C++11)
(C++14)
(C++11)
(C++11)(до C++20*)
(C++11)(устарело в C++20)
(C++11)
Константы свойств типа
Метафункции
(C++17)
Поддерживаемые операции
Запросы отношений и свойств
Модификации типов
(C++11)(C++11)(C++11)
Преобразования типов
(C++11)(устарело в C++23)
(C++11)(устарело в C++23)
(C++11)
(C++11)
(C++17)

common_type
(C++11)
(C++11)(до C++20*)(C++17)
Рациональная арифметика времени компиляции
Целочисленные последовательности времени компиляции
 
Определено в заголовочном файле <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 обозначает этот тип, смотрите условный оператор;
(начиная с 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) (начиная с C++20) из <type_traits> не определено.

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

специализация свойства std::common_type
(специализация шаблона класса) [править]
специализация свойства std::common_type
(специализация шаблона класса) [править]
получает общий тип двух pair
(специализация шаблона класса) [править]
определяет общий тип tuple и tuple-like типов
(специализация шаблона класса) [править]

[править] Возможная реализация

// первичный шаблон (используется для нулевых типов)
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&> {};
 
// Реализация С++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 << "  равно  Number<int>{" << x.n << "}\n";
}
 
void describe(const char *expr, const Number<double> &x) {
    std::cout << expr << "  равно  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  равно  Number<int>{3}
i1 + d2  равно  Number<double>{4.5}
d1 + i2  равно  Number<double>{4.3}
d1 + d2  равно  Number<double>{5.8}

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
LWG 2141 C++11 common_type<int, int>::type равно int&& затухающий тип результата
LWG 2408 C++11 common_type не подходит для SFINAE сделан дружественным к SFINAE
LWG 2460 C++11 специализации common_type почти невозможно написать уменьшено количество необходимых специализаций

[править] Смотрите также

указывает, что два типа имеют общий тип
(концепт) [править]