Заголовок:или<#include<boost/type_traits.hpp>
>
namespace boost {
template <class... T> struct common_type;
}
<common_type
>является классом признаков, используемым для вывода типа, общего для нескольких типов, полезного в качестве возвращаемого типа функций, работающих на нескольких типах ввода, таких как арифметика смешанного режима.
Вложенный типдеф<::type
>можно определить следующим образом:
template <class... T>
struct common_type;
template <class T, class U, class... V>
struct common_type<T, U, V...> {
typedef typename common_type<typename common_type<T, U>::type, V...>::type type;
};
template <>
struct common_type<> {
};
template <class T>
struct common_type<T> {
typedef typename decay<T>::type type;
};
template <class T, class U>
struct common_type<T, U> {
typedef typename decay<
decltype( declval<bool>()?
declval<typename decay<T>::type>():
declval<typename decay<U>::type>() )
>::type type;
};
Все типы параметров должны быть полными. Эта черта может быть специализирована пользователем, если по крайней мере один параметр шаблона является типом, определенным пользователем.Примечание:Такие специализации необходимы, когда среди аргументов<common_type
>требуются только явные преобразования.
Обратите внимание, что когда компилятор не поддерживает вариадные шаблоны (и определяется макрос<BOOST_NO_CXX11_VARIADIC_TEMPLATES
>), то максимальное количество аргументов шаблона составляет 9.
В двух словах,<common_type
>является чертой, которая принимает 1 или более типов и возвращает тип, который все типы преобразуют. Определение по умолчанию требует, чтобы это преобразование было неявным. Однако эта черта может быть специализирована для определенных пользователем типов, которые хотят ограничить свои межтиповые конверсии явными и все же хотят взаимодействовать с объектом<common_type
>.
Пример:
template <class T, class U>
complex<typename common_type<T, U>::type>
operator+(complex<T>, complex<U>);
В приведенном выше примере допускается комплексная арифметика «смешанного режима». Тип возврата описывается<common_type
>. Например, полученный тип добавления<complex<float>
>и<complex<double>
>может быть<complex<double>
>.
Вот как кто-то может создать вариадную функцию сравнения:
template <class ...T>
typename common_type<T...>::type
min(T... t);
Это очень полезная и широко применимая утилита.
Другим вариантом для автора предыдущего оператора может быть
template <class T, class U>
typename common_type<complex<T>, complex<U> >::type
operator+(complex<T>, complex<U>);
Поскольку определение по умолчанию<common_type
>требует, чтобы преобразование было неявным, мы должны специализировать черту для сложных типов следующим образом.
template <class T, class U>
struct common_type<complex<T>, complex<U> > {
typedef complex< common_type<T, U> > type;
};
Порядок параметров шаблона важен.
<common_type<A,B,C>::type
>не является эквивалентом<common_type<C,A,B>::type
>, но<common_type<common_type<A,B>::type,C>::type
>.
рассмотреть
struct A {};
struct B {};
struct C {
C() {}
C(A const&) {}
C(B const&) {}
C& operator=(C const&) {
return *this;
}
};
Следующее не компилируется
typedef boost::common_type<A, B, C>::type ABC;
пока
typedef boost::common_type<C, A, B>::type ABC;
компиляции.
Так как<common_type<A,B>::type
>не определено,<common_type<A,B,C>::type
>также не определено.
Предполагается, что клиенты, которые хотят, чтобы<common_type<A,
B>
>были четко определены, чтобы определить его сами:
namespace boost
{
template <>
struct common_type<A, B> {typedef C type;};
}
Теперь этот клиент может попросить<common_type<A,
B,C>
>(и получить тот же ответ).
Клиенты, желающие запросить<common_type<A,
B,C>
>в любом порядке и получить тот же результат, должны добавить дополнительно:
namespace boost
{
template <> struct common_type<B, A>
: public common_type<A, B> {};
}
Это необходимо, так как специализация<common_type<A,
B>
>не используется неявно для<common_type<B,
A>
>.
Учитывая предыдущий пример, можно ожидать, что<common_type<A,B>::type
>будет<C
>без какого-либо вмешательства со стороны пользователя. Но реализация<common_type<>
>по умолчанию этого не допускает. Предполагается, что клиенты, желающие, чтобы<common_type<A,
B>
>было четко определено, чтобы определить его сами:
namespace boost
{
template <>
struct common_type<A, B> {typedef C type;};
template <> struct common_type<B, A>
: public common_type<A, B> {};
}
Теперь этот клиент может попросить<common_type<A,
B>
>.
рассмотреть
struct C { }:
struct B : C { };
struct A : C { };
Не должно ли быть<common_type<A*,B*>::type
><C*
>? Я бы сказал, что да, но реализация по умолчанию сделает его плохо сформированным.
Библиотека могла бы добавить специализацию для указателей, как
namespace boost
{
template <typename A, typename B>
struct common_type<A*, B*> {
typedef common_type<A, B>* type;
};
}
Но в отсутствие мотивирующих вариантов использования мы предпочитаем не добавлять больше, чем указано в стандарте.
Конечно, пользователь всегда может сделать эту специализацию.
Даже если они кажутся близкими,<common_type
>и<typeof
>имеют разные цели. Вы используете<typeof
>, чтобы получить тип выражения, в то время как вы используете<common_type
>, чтобы явно установить тип возвращаемой функции шаблона. Оба являются взаимодополняющими, и действительно<common_type
>приблизительно эквивалентны<decltype(declval<bool>()
?declval<T>()
:declval<U>())
>.
<common_type
>также аналогично<promote_args<class...T>
>в<boost/math/tools/promotion.hpp
>, хотя это не совсем то же самое, что<promote_args
>.<common_type<T1,T2>::type
>просто представляет собой результат некоторой операции на<T1
>и<T2
>и по умолчанию для типа, полученного путем вставки<T1
>и<T2
>в условное утверждение.
Он должен быть настраиваемым (через специализацию), если этот по умолчанию не подходит.