std::common_type
| ヘッダ <type_traits> で定義
|
||
template< class... T > struct common_type; |
(C++11以上) | |
型 T... のすべてに共通の型、つまり、 T... のいずれもが暗黙に変換できる型を調べます。 そのような型が存在するならば (以下のルールに従って決定されます)、メンバ type はその型を表します。 そうでなければ、メンバ type は存在しません。
sizeof...(T)がゼロの場合、メンバtypeは存在しません。sizeof...(T)が 1 の場合 (つまりT...が型T0ひとつだけを含む場合)、std::common_type<T0, T0>::typeが存在するならばメンバtypeはそれと同じ型を表し、そうでなければメンバtypeは存在しません。sizeof...(T)が 2 の場合 (つまりT...がちょうど2個の型T1およびT2を含む場合)、
T1とT2の少なくとも一方に std::decay を適用すると異なる型が生成される場合、std::common_type<std::decay<T1>::type, std::decay<T2>::type>::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)が 2 より大きい場合 (つまりT...が型T1, T2, R...) から構成される場合)、std::common_type<T1, T2>::typeが存在するならば、メンバtypeはstd::common_type<std::common_type<T1, T2>::type, R...>::typeが存在すればその型を表します。 そうでなければメンバtypeは存在しません。
パラメータパック T 内の型 はいずれも完全型 (またはその cv 修飾された型)、 void、またはサイズの未知な配列でなければなりません。 そうでなければ、動作は未定義です。
上記のテンプレートの実体化が直接または間接的に不完全型に依存しており、もしその型が仮に完全型であったならばその実体化が異なる結果を産むであろう場合は、動作は未定義です。
メンバ型
| 名前 | 定義 |
type
|
T... のすべてに対する共通の型
|
ヘルパー型
<tbody> </tbody> template< class... T > using common_type_t = typename common_type<T...>::type; |
(C++14以上) | |
特殊化
以下の場合、ユーザは型 T1 および T2 に対して common_type を特殊化しても構いません。
T1とT2の少なくとも一方がユーザ定義型に依存し、さらにT1とT2の両方に対して std::decay が恒等変換である。
そのような特殊化が type という名前のメンバを持つ場合、それは T1 と T2 の両方が明示的に変換可能な、 cv 修飾されていない非参照型を表す、パブリックな曖昧でないメンバ型でなければなりません。 さらに、 std::common_type<T1, T2>::type と std::common_type<T2, T1>::type は同じ型を表さなければなりません。
これらのルールに違反する common_type の特殊化を追加するプログラムは、未定義動作を持ちます。
common_type 以外の <type_traits> で定義されているテンプレートに特殊化を追加するプログラムの動作は未定義であることに注意してください。
以下の特殊化は標準ライブラリによってあらかじめ提供されています。
| std::common_type 特性の特殊化 (クラステンプレートの特殊化) | |
| std::common_type 特性の特殊化 (クラステンプレートの特殊化) |
実装例
// primary template (used for zero types)
template <class ...T>
struct common_type {};
//////// one type
template <class T>
struct common_type<T> : common_type<T, T> {};
//////// two types
// default implementation for two types
template<class T1, class T2>
using cond_t = decltype(false ? std::declval<T1>() : std::declval<T2>());
template<class T1, class T2, class=void>
struct common_type_2_default {};
template<class T1, class T2>
struct common_type_2_default<T1, T2, std::void_t<cond_t<T1, T2>>> {
using type = std::decay_t<cond_t<T1, T2>>;
};
// dispatcher to decay the type before applying specializations
template<class T1, class T2, class D1 = std::decay_t<T1>, class D2=std::decay_t<T2>>
struct common_type_2_impl : common_type<D1, D2> {};
template<class D1, class D2>
struct common_type_2_impl<D1, D2, D1, D2> : common_type_2_default<D1, D2> {};
template <class T1, class T2>
struct common_type<T1, T2> : common_type_2_impl<T1, T2> { };
//////// 3+ types
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<std::void_t<common_type_t<T1, T2>>, T1, T2, R...>
: common_type<common_type_t<T1, T2>, R...> { };
template <class T1, class T2, class... R>
struct common_type<T1, T2, R...>
: common_type_multi_impl<void, T1, T2, R...> { };
|
ノート
整数拡張の対象でない算術型の場合、 common_type は T0() + T1() + ... + Tn() のような (型が混在しているかもしれない) 算術式の型としてみることができます。
欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
| DR | 適用先 | 発行時の動作 | 正しい動作 |
|---|---|---|---|
| LWG 2141 | C++11 | common_type<int, int>::type is int&&
|
decayed result type |
| LWG 2408 | C++11 | common_type is not SFINAE-friendly
|
made SFINAE-friendly |
| LWG 2460 | C++11 | common_type specializations are nearly impossible to write
|
reduced number of specializations needed |
例
ユーザ定義クラスに対する型混在算術をデモンストレーションします。
#include <iostream>
#include <type_traits>
template <class T>
struct Number { T n; };
template <class T, class U>
Number<typename std::common_type<T, U>::type> operator+(const Number<T>& lhs,
const Number<U>& rhs)
{
return {lhs.n + rhs.n};
}
int main()
{
Number<int> i1 = {1}, i2 = {2};
Number<double> d1 = {2.3}, d2 = {3.5};
std::cout << "i1i2: " << (i1 + i2).n << "\ni1d2: " << (i1 + d2).n << '\n'
<< "d1i2: " << (d1 + i2).n << "\nd1d2: " << (d1 + d2).n << '\n';
}
出力:
i1i2: 3
i1d2: 4.5
d1i2: 4.3
d1d2: 5.8