C++17では<type_traits>
に機能追加が行われた。
C++17では、既存のtraits
に変数テンプレートを利用した_v
版が追加された。
たとえば、is_integral<T>::value
と書く代わりにis_integral_v<T>
と書くことができる。
template < typename T >
void f( T x )
{
constexpr bool b1 = std::is_integral<T>::value ; // データメンバー
constexpr bool b2 = std::is_integral_v<T> ; // 変数テンプレート
constexpr bool b3 = std::is_integral<T>{} ; // operator bool()
}
C++17ではクラステンプレートconjunction
, disjunction
, negation
が追加された。これはテンプレートメタプログラミングで論理積、論理和、否定を手軽に扱うためのtraits
だ。
template<class... B> struct conjunction;
クラステンプレートconjunction<B1, B2, ..., BN>
はテンプレート実引数B1
, B2
, ..., BN
に論理積を適用する。conjunction
はそれぞれのテンプレート実引数Bi
に対して、bool(Bi::value)
がfalse
となる最初の型を基本クラスに持つか、あるいは最後のBN
を基本クラスに持つ。
int main()
{
using namespace std ;
// is_void<void>を基本クラスに持つ
using t1 =
conjunction<
is_same<int, int>, is_integral<int>,
is_void<void> > ;
// is_integral<double>を基本クラスに持つ
using t2 =
conjunction<
is_same<int, int>, is_integral<double>,
is_void<void> > ;
}
template<class... B> struct disjunction;
クラステンプレートdisjunction<B1, B2, ..., BN>
はテンプレート実引数B1
, B2
, ..., BN
に論理和を適用する。disjunction
はそれぞれのテンプレート実引数Bi
に対して、bool(Bi::value)
がtrue
となる最初の型を基本クラスに持つか、あるいは最後のBN
を基本クラスに持つ。
int main()
{
using namespace std ;
// is_same<int,int>を基本クラスに持つ
using t1 =
disjunction<
is_same<int, int>, is_integral<int>,
is_void<void> > ;
// is_void<int>を基本クラスに持つ
using t2 =
disjunction<
is_same<int, double>, is_integral<double>,
is_void<int> > ;
}
template<class B> struct negation;
クラステンプレートnegation<B>
はB
に否定を適用する。negation
は基本クラスとしてbool_constant<!bool(B::value)>
を持つ。
int main()
{
using namespace std ;
// false
constexpr bool b1 = negation< true_type >::value ;
// true
constexpr bool b2 = negation< false_type >::value ;
}
template <class Fn, class... ArgTypes>
struct is_invocable;
template <class R, class Fn, class... ArgTypes>
struct is_invocable_r;
template <class Fn, class... ArgTypes>
struct is_nothrow_invocable;
template <class R, class Fn, class... ArgTypes>
struct is_nothrow_invocable_r;
is_invocable
はテンプレート実引数で与えられた型Fn
がパラメーターパックArgTypes
をパック展開した結果を実引数に関数呼び出しできるかどうか、そしてその戻り値はR
へ暗黙変換できるかどうかを確認するtraits
だ。呼び出せるのであればtrue_type
, そうでなければfalse_type
を基本クラスに持つ。
is_invocable
は関数呼び出しした結果の戻り値の型については問わない。
is_invocable_r
は呼び出し可能性に加えて、関数呼び出しした結果の戻り値の型がR
へ暗黙変換できることが確認される。
is_nothrow_invocable
とis_nothrow_invocable_r
は、関数呼び出し(および戻り値型R
への暗黙変換)が無例外保証されていることも確認する。
int f( int, double ) ;
int main()
{
// true
constexpr bool b1 =
std::is_invocable< decltype(&f), int, double >{} ;
// true
constexpr bool b2 =
std::is_invocable< decltype(&f), int, int >{} ;
// false
constexpr bool b3 =
std::is_invocable< decltype(&f), int >{} ;
// false
constexpr bool b4 =
std::is_invocable< decltype(&f), int, std::string >{} ;
// true
constexpr bool b5 =
std::is_invocable_r< int, decltype(&f), int, double >{} ;
// false
constexpr bool b6 =
std::is_invocable_r< double, decltype(&f), int, double >{} ;
}
template <class T>
struct has_unique_object_representations ;
has_unique_object_representations<T>
は、T
型がトリビアルにコピー可能で、かつT
型の同値である2つのオブジェクトの内部表現が同じ場合に、true
を返す。
false
を返す例としては、オブジェクトがパディング(padding)と呼ばれるアライメント調整などのための値の表現に影響しないストレージ領域を持つ場合だ。パディングビットの値は同値に影響しないので、false
を返す。
たとえば以下のようなクラスX
は、
struct X
{
std::uint8_t a ;
std::uint32_t b ;
} ;
ある実装においては、4バイトにアライメントする必要があり、そのオブジェクトの本当のレイアウトは以下のようになっているかもしれない。
struct X
{
std::uint8_t a ;
std::byte unused_padding[3] ;
std::uint32_t b ;
} ;
この場合、unused_padding
の値には意味がなく、クラスX
の同値比較には用いられない。この場合、std::has_unique_representations_v<X>
はfalse
になる。
template <class T>
struct is_nothrow_swappable;
template <class T, class U>
struct is_nothrow_swappable_with;
is_nothrow_swappable<T>
はT
型がswap
で例外を投げないときにtrue
を返す。
is_nothrow_swappable_with<T, U>
は、T
型とU
型を相互にswap
するときに例外を投げないときにtrue
を返す。