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 соответственно const std::remove_reference_t<T1>& и const std::remove_reference_t<T2>&, то член 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::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++.

DR Применён к Поведение, как опубликовано Правильное поведение
LWG 2141 C++11 common_type<int, int>::type это int&& тип результата с применением 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/w/cpp/types/common_type

Spec-Zone.ru

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