C++メタ関数のまとめ

#メタ関数のまとめ

##メタ関数とは？
メタ関数とは、コンパイル時に型情報を取得できる関数のようなもの
実際には、関数の形を取らず、クラスになっていることがほとんどだが
関数にすることもできる

例えば型`T`と型`U`が同一かどうかを確かめたい場合は

```cpp
std::is_same<T,U>::value
```

とすれば
is_sameはT,Uが同じ型ならネストされたメンバ変数valueはtrue
T,Uが違う型ならfalseを返す

他には、どのようなことができるのか？
標準ライブラリを見ていこう！


##標準ライブラリ\<type_traits\>のメタ関数一覧
メタ関数はC++11から追加された標準ライブラリ\<type_traits\>に定義されている
C++14では**is_nullptr** と**is_final**が仲間入りした

C++17からは**is_literal_type**、が**非推奨**になる
また、
**has_unique_object_representations**、
**is_swappable_with**、**is_swappable**、**is_nothrow_swappable_with**、**is_nothrow_swappable**、
**is_callable**、**is_nothrow_callable**、
**conjunction**、**disjunction**、**negation**、
**bool_constant**、**void_t**が仲間入りする

####一覧の見方

テンプレート宣言は省略。
かわりにテンプレート仮引数にclassなどの修飾をする。
例えば、

```cpp
template < class T, T value >
class integral_constant;
```

は

```cpp
integral_constant<class T, T value>
```

と表記する。

###ヘルパークラス
---
<dl>
<dt>integer_constant&lt;class T, T value&gt;</dt>
<dd>T型の整数定数を表す型</dd>
<dt>true_type</dt>
<dd>trueを表す型</dd>
<dt>false_type<dt>
<dd>falseを表す型</dd>
<dt>bool_constant&lt;bool B&gt;(Since C++17)</dt>
<dd>bool型の整数定数を表す型</dd>
</dl>

###基本的な型
---

<dl>
<dt>is_void&lt;class T&gt;</dt>
<dd>T型が void であるかどうかをテストします</dd>
<dt>is_nullptr&lt;class T&gt;(since C++14)</dt>
<dd>T型が nullptr_t であるかどうかをテストします</dd>
<dt>is_integral&lt;class T&gt;</dt>
<dd>T型が整数型であるかどうかをテストします</dd>
<dt>is_floating_point&lt;class T&gt;</dt>
<dd>T型が浮動小数点型であるかどうかをテストします/<dd>
<dt>is_array&lt;class T&gt;</dt>
<dd>T型が配列型であるかどうかをテストします</dd>
<dt>is_pointer&lt;class T&gt;</dt>
<dd>T型がポインターであるかどうかをテストします</dd>
<dt>is_lvalue_reference&lt;class T&gt;</dt>
<dd>T型が lvalue 参照であるかどうかをテストします</dd>
<dt>is_rvalue_reference&lt;class T&gt;</dt>
<dd>T型が rvalue 参照であるかどうかをテストします</dd>
<dt>is_member_function_pointer&lt;class T&gt;</dt>
<dd>T型がメンバー関数へのポインターであるかどうかをテストします</dd>
<dt>is_member_object_pointer&lt;class T&gt;</dt>
<dd>T型がメンバー オブジェクトへのポインターであるかどうかをテストします</dd>
<dt>is_enum&lt;class T&gt;</dt>
<dd>T型が列挙型であるかどうかをテストします</dd>
<dt>is_union&lt;class T&gt;</dt>
<dd>T型が共用体であるかどうかをテストします</dd>
<dt>is_class&lt;class T&gt;</dt>
<dd>T型がクラスであるかどうかをテストします</dd>
<dt>is_function&lt;class T&gt;</dt>
<dd>T型が関数型であるかどうかをテストします</dd>
</dl>

###複合型
---
<dl>
<dt>is_scalar&lt;class T&gt;</dt>
<dd>T型がスカラーであるかどうかをテストします</dd>
<dt>is_arithmetic&lt;class T&gt;</dt>
<dd>T型が演算型であるかどうかをテストします</dd>
<dt>is_reference&lt;class T&gt;</dt>
<dd>T型が参照であるかどうかをテストします</dd>
<dt>is_fundamental&lt;class T&gt;</dt>
<dd>T型が void または演算型であるかどうかをテストします</dd>
<dt>is_member_pointer&lt;class T&gt;</dt>
<dd>T型がメンバーへのポインターであるかどうかをテストします</dd>
<dt>is_object&lt;class T&gt;</dt>
<dd>T型がオブジェクト型であるかどうかをテストします</dd>
<dt>is_compound&lt;class T&gt;</dt>
<dd>T型が非スカラー（複合型）であるかどうかをテストします</dd>
</dl>

###型の特性
---

<dl>
<dt>is_abstract&lt;class T&gt;</dt>
<dd>T型が抽象クラスであるかどうかをテストします</dd>
<dt>is_const&lt;class T&gt;</dt>
<dd>T型が定数型であるかどうかをテストします</dd>
<dt>is_empty&lt;class T&gt;</dt>
<dd>T型が空のクラスであるかどうかをテストします</dd>
<dt>is_final&lt;class T&gt;(since C++14)</dt>
<dd>T型にfinalが付いているかどうかをテストします</dd>
<dt>is_pod&lt;class T&gt;</dt>
<dd>T型が POD であるかどうかをテストします</dd>
<dt>is_literal_type&lt;class T&gt;(deprecated in C++17)</dt>
<dd>T型がリテラル型かどうかをテストします</dd>
<dt>is_polymorphic&lt;class T&gt;</dt>
<dd>T型に仮想関数が存在するかどうかをテストします</dd>
<dt>is_signed&lt;class T&gt;</dt>
<dd>T型が符号付き整数であるかどうかをテストします</dd>
<dt>is_standard_layout&lt;class T&gt;</dt>
<dd>T型が標準レイアウト型であるかどうかをテストします</dd>
<dt>is_unsigned&lt;class T&gt;</dt>
<dd>T型が符号なし整数であるかどうかをテストします</dd>
<dt>Tis_volatile&lt;class T&gt;</dt>
<dd>型が volatile であるかどうかをテストします</dd>
</dl>


###型のサポートするオペレーション
---

型が特定のオペレーションをサポートしているかをテストする
特にコンストラクト、コピー、ムーブ、デストラクトの4種類のオペレーションについては

通常版
トリビアル版
無例外保証版

の3種類がある

<dl>
<dt>is_constructible&lt;class T, class... Args&gt;</dt>
<dt>is_trivially_constructible&lt;class T, class... Args&gt;</dt>
<dt>is_nothrow_constructible&lt;class T, class... Args&gt;</dt>
<dd>指定された初期化子型リスト（Args...）からT型がコンストラクトできるかをテストする</dd>
<dt>is_default_constructible&lt;class T&gt;</dt>
<dt>is_trivially_default_constructible&lt;class T&gt;</dt>
<dt>is_nothrow_default_constructible&lt;class T&gt;</dt>
<dd>T型がデフォルトコンストラクトできるかをテストする</dd>
<dt>is_copy_constructible&lt;class T&gt;</dt>
<dt>is_trivially_copy_constructible&lt;class T&gt;</dt>
<dt>is_nothrow_copy_constructible&lt;class T&gt;</dt>
<dd>T型がコピーコンストラクトできるかをテストする</dd>
<dt>is_move_constructible&lt;class T&gt;</dt>
<dt>is_trivially_move_constructible&lt;class T&gt;</dt>
<dt>is_nothrow_move_constructible&lt;class T&gt;</dt>
<dd>T型がムーブコンストラクトできるかをテストする</dd>
<dt>is_assignable&lt;class T, class U&gt;</dt>
<dt>is_trivially_assignable&lt;class T, class U&gt;</dt>
<dt>is_nothrow_assignable&lt;class T, class U&gt;</dt>
<dd>T型にU型が代入演可能かどうかをテストする</dd>
<dt>is_copy_assignable&lt;class T&gt;</dt>
<dt>is_trivially_copy_assignable&lt;class T&gt;</dt>
<dt>is_nothrow_copy_assignable&lt;class T&gt;</dt>
<dd>T型がコピー代入演可能かどうかをテストする</dd>
<dt>is_destructible&lt;class T&gt;</dt>
<dt>is_trivially_destructible&lt;class T&gt;</dt>
<dt>is_nothrow_destructible&lt;class T&gt;</dt>
<dd>T型が明示的にdeleted宣言されていないデストラクタをもつかテストする</dd>
<dt>has_virtual_destructor&lt;class T&gt;</dt>
<dd>T型がvirtualなデストラクタを持つかテストする</dd>
<dt>is_swappable_with&lt;class T, class U&gt;(since C++17)</dt>
<dd>T型がU型とスワップできるかをテストします</dd>
<dt>is_swappable&lt;class T&gt;(since C++17)</dt>
<dd>T型が同じ型とスワップできるかをテストします</dd>
<dt>is_nothrow_swappable_with&lt;class T, class U&gt;(since C++17)</dt>
<dd>T型がU型と無例外保証付きでスワップできるかをテストします</dd>
<dt>is_nothrow_swappable&lt;class T&gt;(since C++17)</dt>
<dd>T型が同じ型と無例外保証付きでスワップできるかをテストします</dd>
<dt>has_unique_object_representations&lt;class T&gt;(since C++17)</dt>
<dd>オブジェクトから自動的にハッシュを計算できるかをテストする（C++17では自動ハッシュ計算をサポートしないが、その前段階として入った機能である）</dd>
</dl>

###型の特性についての問い合わせ
---
<dl>
<dt>alignment_of&lt;class T&gt;</dt>
<dd>T型のアライメントを取得します</dd>
<dt>rank&lt;class T&gt;</dt>
<dd>配列の次元数を取得します</dd>
<dt>extent&lt;class T, size_t I = 0&gt;</dt>
<dd>配列のI番目の次元を取得します</dd>
</dl>

###型の関係
---
<dl>
<dt>is_same&lt;class T, class U&gt;</dt>
<dd>2 つの型が等しいかどうかをテストします</dd>
<dt>is_base_of&lt;class Base, class Derrived&gt;</dt>
<dd>Base型がDerrived型の基本クラスであるかどうかをテストします</dd><dt>is_convertible&lt;class From, class To&gt;</dt>
<dd>From型をTo型に変換できるかどうかをテストします</dd>
<dt>is_callable&lt;F(Args...)&gt;(since C++17)</dt>
<dd>関数が呼び出せるかどうかをテストします</dd>
<dt>is_nothrow_callable&lt;F(Args...)&gt;(since C++17)</dt>
<dd>関数が無例外保証付きで呼び出せるかどうかテストします</dd>
</dl>

###const/volatileの変更
---
<dl>
<dt>add_const&lt;class T&gt;</dt>
<dd>T型から const T型を作成します</dd>
<dt>add_volatile&lt;class T&gt;</dt>
<dd>T型から volatile T型を作成します</dd>
<dt>add_cv&lt;class T&gt;</dt>
<dd>型から const volatile 型を作成します</dd>
<dt>remove_const&lt;class T&gt;</dt>
<dd>型から非 const 型を作成します</dd>
<dt>remove_volatile&lt;class T&gt;</dt>
<dd>型から非 volatile 型を作成します</dd>
<dt>remove_cv&lt;class T&gt;</dt>
<dd>型から非 const volatile 型を作成します</dd>
</dl>

###referenceの変更
---
<dl>
<dt>add_lvalue_reference&lt;class T&gt;</dt>
<dd>T型からT型への左辺値参照(T&)を作成します</dd>
<dt>add_rvalue_reference&lt;class T&gt;</dt>
<dd>T型からT型への右辺値参照(T&&)を作成します</dd>
<dt>remove_reference&lt;class T&gt;</dt>
<dd>型から非参照型を作成します</dd>
</dl>

###配列の変更
---
<dl>
<dt>remove_extent&lt;class T&gt;</dt>
<dd>配列型から次元を取り除き要素型を作成します</dd>
<dt>remove_all_extents&lt;class T&gt;</dt>
<dd>配列型からすべての次元を取り除き非配列型を作成します</dd>
</dl>

###ポインタの変更
---
<dl>
<dt>add_pointer&lt;class T&gt;</dt>
<dd>T型から型へのポインタ(T*)を作成します</dd>
<dt>remove_pointer&lt;class T&gt;</dt>
<dd>型へのポインタから型を作成します</dd>
</dl>

###符号の変更
---
<dl>
<dt>make_signed&lt;class T&gt;</dt>
<dd>符号付きの型を作成します</dd>
<dt>make_unsigned&lt;class T&gt;</dt>
<dd>符号なしの型を作成します</dd>
</dl>

###その他の変更
---
<dl>
<dt>aligned_storage&lt;size_t Len, size_t Align = -default-align&gt;</dt>
<dd>アライメント調整された領域を作成します</dd>
<dt>aligned_union&lt;size_t Len, class... Types&gt;</dt>
<dd>アライメント調整された共用体領域を作成します</dd>
<dt>common_type&lt;class... Typesgt;</dt>
<dd>2つ以上の型の型変換可能な共通型のインスタンスを作成します</dd>
<dt>conditional&lt;bool B, class T, class F&gt;</dt>
<dd>コンパイル時条件式, BがtrueならT, false ならFをtypeに持つ</dd>
<dt>decay&lt;class T&gt;</dt>
<dd>非参照、非定数、非揮発の型、または型へのポインターを作成します<br>（配列と関数を通常のテンプレート型推論と同様に推論する場合にも用いる）</dd>
<dt>enable_if&lt;bool B, class T = void&gt;</dt>
<dd>条件が真の場合有効な型Tに実体化します</dd>
<dt>underlying_type&lt;class E&gt;</dt>
<dd>enumの基底の型を作成します</dd>
<dt>result_of&lt;f(Args...)&gt;</dt>
<dd>関数の戻り値の型を作成します</dd>
<dt>void_t&lt;class... Dummy&gt;(since C++17)</dt>
<dd>テンプレート引数に何を与えてもvoidに実体化します</dd>
</dl>

###コンパイル時条件の論理演算(since C++17)
---
<dl>
<dt>conjunction&lt;class... B&gt;(since C++17)</dt>
<dd>コンパイル時条件の可変長AND演算を行います</dd>
<dt>disjunction&lt;class... B&gt;(since C++17)</dt>
<dd>コンパイル時条件の可変長OR演算を行います</dd>
<dt>negation&lt;class B&gt;(since C++17)</dt>
<dd>コンパイル時条件のNOT演算を行います</dd>
</dl>
##エイリアステンプレート
型から型を作成するメタ関数は

```cpp
typename add_const<T>::type
```
のようにしてネストされた型を取り出すが
面倒なのでエイリアステンプレートが定義されている
以下のようになっている

```cpp
template < typename T >
using add_const_t = typename add_const<T>::type ;
```

`typename` と `::type` が両方とも省ける！
`add_const_t<T>`などと書ける
やったぜ！

##変数テンプレート
`constexpr` と `Variable template` 導入によっては型情報をbool値で返す
メタ関数を

```cpp
template < typename T, typename U >
constexpr bool is_same_v = is_same<T,U>::value ;
```

と記述できるようになった

これで
`is_same_v<T,U>`などと書ける
ちょい便利です。
この機能はC++17から標準ライブラリに入ることになっている


#神でもわかるメタ関数の作り方
C++11以前では`sizeof`を悪用した一見意味の分からないコードで
メタ関数を記述していた
しかし、C++11から`decltype`が入り
`declval`が入り
とにかく簡単にかけるようになった

##C++11以前のメタ関数
boost::is_convertibleの実装である
基本的にsizeofとオーバーロード解決の順序を使う

```cpp
template <typename From, typename To>
struct is_convertible_basic_impl
{
    // 2 つの同名関数を作って
    static no_type _m_check(...);
    static yes_type _m_check(To);

    // 関数の戻り値の型を見る（どっちの関数が使われるかを見る）
    static bool value = sizeof( _m_check(From) ) == sizeof(yes_type);
};
```

##C++11以降のメタ関数

**SFINAE** という強力な武器を得た
これは、Substitution Failure Is Not An Errorの略語である
（テンプレートの）実体化の失敗はエラーではない、という意味である
と言ってもわからないだろうから、例を出して説明する

```cpp
template < typename T >
typename T::value_type func(T);
```

上の関数の戻り値に`typename T::value_type`という型が用いられている
引数は`T`であるから、あらゆる型を推論できるが`value_type`というメンバ型名を持っていない場合にはどうなるのだろう？
ここで **SFINAE** が発動する
テンプレートの実体化に失敗した場合は即座にエラーにせず、他の関数が一致しないかを探すのである

このSFIANEはdecltypeの文脈でも適用される

```cpp
template < typename L, typename R >
auto plus(L&& l, R&& r) -> decltype(l+r)
{
  return l + r;
}
```

戻り値の後置宣言を用いて、`decltype(任意の式)`とすることで、任意の式が有効な場合に実体化に成功し、無効の場合実体化に成功するという関数になる

ただし重要なことは、実体化に成功する関数が同時に複数あるとオーバーロードの解決に失敗するため、適切に実体化を失敗させるテクニックが必要であり、どう考えても玄人しか使えないという点である。

C++11以降のメタ関数ではこれらを駆使する。

##特定のクラスかどうかを判定する
クラスの完全な特殊化を用いる

```cpp
template < typename T >
struct is_int {
    static constexpr bool value = false ;
}
template < >
struct is_int<int> {
    static constexpr bool value = true ;
}
```

##複合型の判定（ポインタ型の判定など）
ポインタ型の判定をしたいときはクラスの部分的特殊化を利用する

```cpp
template < typename T >
struct is_pointer {
    static constexpr bool value = false ;
}
template < typename T >
struct is_int<T*> {
    static constexpr bool value = true ;
}
```

これだけなら`std::is_pointer<T>`を使えばいい
部分的特殊化を使う場合は判定したいクラスがテンプレートになっているときだ

`std::vector<T>`かどうかを判定するメタ関数を書きたいとする
このとき**T**はなんでもかまわないので部分的特殊化が使える

```cpp
template < typename T >
struct is_vector : std::false_type{};

template < typename T >
struct is_vector<std::vector<T>> : std::true_type {};

template < typename T >
constexpr bool is_vector_v = is_vector<T>::value;
```

また、`std::tuple`かどうかを判定したい場合はtupleが可変長テンプレートなので、可変長テンプレートで部分的特殊化を用いる

```cpp
template < typename T >
struct is_tuple : std::false_type{};

template < typename ...Types >
struct is_tuple<std::tuple<Types...>> : std::true_type {};

template < typename T >
constexpr bool is_tuple_v = is_tuple<T>::value;
```


##メンバ型名を持っているか判定する
関数のオーバーロード解決（SFINAE）を利用する
クラスが`iterator`を持っているかどうかを判定したいときは


```cpp
template <class T>
class has_iterator {
  template <class U>
  static constexpr bool check(typename U::iterator*)
  { return true; }

  template <class U>
  static constexpr bool check(...)
  { return false; }
public:
  static constexpr bool value = check<T>(nullptr);
};
```
上の関数ではSFINAEのトリックを使っている
`U`が`iterator`メンバを持っていない場合
実体化に失敗しオーバーロード解決の候補から外されて
下の関数が呼ばれることになる



関数に定義はもはや必要ではない
われわれには`decltype`がある
そして、ヘルパークラスの
`std::true_type`と`std::false_type`がある

```cpp
template <class T>
class has_iterator {
  template <class U>
  static constexpr std::true_type check(typename U::iterator*);

  template <class U>
  static constexpr std::false_type check(...);

public:
  static constexpr bool value = decltype(check<T>(nullptr))::value;
};
```

ちなみにこのSFINAEのトリックをテンプレートのデフォルト引数にすることもできる
オーバーロードが優先されれば引数はなんでも良いので
例えば`int`と`long`でもよい

```cpp
template <class T>
class has_iterator {
  template <class U, typename O = typename U::iterator>
  static constexpr std::true_type check(int);

  template <class U>
  static constexpr std::false_type check(long);

public:
  static constexpr bool value = decltype(check<T>(0))::value;
};
```
---


###ちょっと一捻りして継承を使う

```cpp

struct has_iterator_impl {
  template <class T>
  static std::true_type check(typename T::iterator*);

  template <class T>
  static std::false_type check(...);
};

template <class T>
class has_iterator :
  public decltype(has_iterator_impl::check<T>(nullptr)) {};

```


##型名以外のメンバを持っているかを判定する
型名以外のメンバとは
変数と関数である

###メンバ変数を持っているかを確かめたい場合

```cpp
struct has_value_impl {
  template <class T>
  static std::true_type check(decltype(T::value)*);

  template <class T>
  static std::false_type check(...);
};

template <class T>
class has_value :
  public decltype(has_value_impl::check<T>(nullptr)) {};

```
---

###メンバ関数`f()`を持っているかを確かめたい場合

```cpp
struct has_f_impl {
  template <class T>
  static auto check(T&& x)->decltype(x.f(),std::true_type{});

  template <class T>
  static auto check(...)->std::false_type;
};

template <class T>
class has_f :
  public decltype(has_f_impl::check<T>(std::declval<T>())) {};

```
これは`decltype`とカンマ演算子`operator,`を利用したSFINAEトリックである
__`decltype`が引数を2つとるわけではない！__

`decltype`の中の式はカンマ演算子によって左から右に順次評価され
返る値は一番右の結果になります
つまり
`x.f()`の呼び出しに失敗すれば
SFINAEによってオーバーロード解決の候補から外され
成功すれば
`std::true_type{}`を評価し`decltype`の表す型は`std::true_type`になります


---
###カンマ演算子
コンマ演算子の結合規則は、左から右方向です。コンマで区切られた 2 つの式は左から右に評価されます。左オペランドは常に評価され、右オペランドが評価される前にすべての副作用が完了します。

コンマは、関数の引数リストなどの一部のコンテキストで、区切り記号として使用できます。区切り記号としてのコンマの使用と演算子としての使用を混同しないでください。この 2 つの用途は、まったく別のものです。

次の式を考えます。

`e1 , e2`

この式の型と値は、e2 の型と値です。e1 を評価した結果は破棄されます。結果は、右オペランドが左辺値の場合は左辺値です。

通常、コンマが区切り記号として使用される場所 (たとえば、関数の実引数や集約の初期化子) では、コンマ演算子とそのオペランドをかっこで囲む必要があります。 たとえば、

```cpp
func_1( x, y + 2, z );
func_2( (x--, y + 2), z );
```

上の func_1 の関数呼び出しでは、x、y + 2、z という 3 つの引数がコンマで区切られて渡されます。 func_2 の関数呼び出しでは、かっこにより、コンパイラは順次評価演算子として最初のコンマを解釈します。この関数呼び出しは、func_2 に 2 つの引数を渡します。最初の引数は、順次評価演算 (x--, y + 2) の結果です。この演算は、式 y + 2 の値と型を持ち、第 2 の引数は z です。

---

###関数`value()`と変数`value`のどちらかを持っているかを確かめたい場合

クラスが持つメンバは型、関数、変数である
この内で型にはなく、関数と変数に共通してもつ性質を利用する
すなわち、ポインタを持つという性質を利用する

```cpp
struct has_value_impl {
  template <class T>
  static std::true_type check(decltype(&T::value));

  template <class T>
  static std::false_type check(...);
};

template <class T>
class has_value :
  public decltype(has_value_impl::check<T>(nullptr)) {};

```



##型の特性
`T`が代入可能かどうかを確かめたい場合


```cpp
struct is_assignable_impl {
  template <class T>
  static auto check(T&& x, T const& y) -> decltype(x=y,std::true_type{});

  static auto check(...) -> std::false_type;
};

template <class T>
struct is_assignable
  : decltype(is_assignable_impl::check(std::declval<T>(),std::declval<T>())) {};
```

###declval
ここで`std::declval<T>()`なるものが出てきました
式が有効になるかどうかは`decltype`な中に式を記述する
SFINAEトリックを用いれば良いのでした
ただ、式を記述するには変数が必要です
型の特性を調べるのはコンパイル時なので変数を作ることはできません
しかし、`decltype`のなかでは本当の変数は必要ないのです

```cpp
template < typename T >
std::add_rvalue_reference<T> value() ;
```

のような関数を作れば返り値を`decltype`の中で変数として用いることができます
これこそ`declval`のやっていることです
`declval`は`decltype`の中でのみ用いることができる変数を作る関数です

##C++1z時代のメタ関数 [ 追記（2016-9-17）]

C++も順調に進化して、メタ関数の書き方も変遷している。
C++1zに採択された`void_t`について追記する。

###始まりはここから（適当

```cpp
template<typename T, typename = void>
struct is_equality_comparable : std::false_type
{};

template<typename T>
struct is_equality_comparable<T,
    typename std::enable_if<
        true, 
        decltype(std::declval<T&>() == std::declval<T&>(), (void)0)
        >::type
    > : std::true_type
{};
```

型`T`が`operator==`で比較できるかを調べるメタ関数である。
特殊化された下のクラスは`std::enable_if`の条件を常に真にして`decltype`の中で条件を記述する。そういておいて、条件が恙無く評価できればカンマ演算子で`void`に推論させている。

明らかに`std::enable_if`の使い方を間違っている。
実体化に成功したら常に`void`になる型がアレばいいのではないか？

そう、そこで`void_t`の爆誕である。

```cpp
template < typename ... >
using void_t = void;
```
`void_t`にどのような仮引数を渡しても`void`に実体化する。
これを用いて、先の`is_equality_comparable`を書き直そう。

```cpp
template< class, class=void >
struct is_equality_comparable : std::false_type
{};

template< class T >
struct is_equality_comparable<T,
  void_t<decltype(std::declval<T&>() == std::declval<T&>() )>
  > : std::true_type
{};

```

基底となるクラスのテンプレート宣言は

```cpp
template <class,class=void>
```

とし、`std::false_type`を継承する。

次に特殊化されたクラスを作る。
テンプレート宣言を

```cpp
template <class T>
```

とする。
ここで`void_t`の出番である。

特殊化の引数の1つめは`T`にしておき、2つめを`void_t`にする。
`void_t`のテンプレート引数に`decltype`を指定し、その中に確かめたいことを書く。



```cpp
struct is_equality_comparable<T,
  void_t<decltype(std::declval<T&>() == std::declval<T&>() )>
```

今回は`operator==`で比較できるかを書いたが、この手法で特定のメンバを持っているかや、特定の操作が可能かどうかをコンパイル時に確かめることができる。

**Detection Idiom**と呼ばれるテクニックだ。

もっと詳しく詳しく知りたいなら以下を参考にすれば良いだろう。
[Faith and Brave](http://faithandbrave.hateblo.jp/entry/2016/09/05/170810)
[yohhoyの日記](http://d.hatena.ne.jp/yohhoy/20151103/p1)
[N3911 TransformationTrait Alias void_t](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf)

以下はC++1zには採択されなかった`void_t`を用いたツールキットの提案である。
なかなか便利なのだが、やり過ぎ感が漂っている。
[N4502 Proposing Standard Library Support for the C++ Detection Idiom, v2](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf)


##コンパイル時の条件演算

###bool_constant

・条件Aか条件Bを満たす場合に真となるメタ関数
`std::integral_constant<bool,value>`を継承してしまいましょう。`std::integral_constant<bool,value>`のエイリアス`std::bool_constant<value>`がつかえます（since C++17）。
例えば、**Range**であるかどうかを雑に判定するとして、`T`型のオブジェクト`a`に対して

**条件A**

```cpp
std::begin(a), std::end(a)
```

が可能
もしくは

**条件B**

```cpp
begin(a), end(a)
```

が可能であれば良い。
そこで、この片方が可能であるかどうかを判定するメタ関数をそれぞれ書く。

最後に

```cpp
template < typename T >
struct is_range : std::bool_constant<
  A<T>::value || B<T>::value
>{};
```

のようにすれば良い。
以下、実装例。


```cpp
#include <type_traits>
#include <utility>

namespace cranberries_magic{

  template < class, class=void >
  struct enable_std_begin_end : std::false_type {};
  
  template < typename T >
  struct enable_std_begin_end<T,
      std::void_t<decltype( std::begin(std::declval<const T&>()),std::end(std::declval<const T&>()) )>>
  : std::true_type {};

  template < class, class=void >
  struct enable_adl_begin_end : std::false_type {};
  
  template < typename T >
  struct enable_adl_begin_end<T,
      std::void_t<decltype( begin(std::declval<const T&>()),end(std::declval<const T&>()) )>>
  : std::true_type {};


} // ! namespace cranberries_magic

  template < typename T >
  struct is_range
    : std::bool_constant<
       cranberries_magic::enable_std_begin_end<T>::value
    || cranberries_magic::enable_adl_begin_end<T>::value>
  {};

  template < typename T >
  constexpr bool is_range_v = is_range<T>::value;
```

###enable_if_tを使った場合

`is_iterator`を書いてみた例。
`iterator_tag`として`input_iterator_tag`もしくは、`output_iterator_tag`を継承していれば`iterator`であるはずという、雑な判定である。
`bool_constant`との違いはクラスが２つに別れているところだ。


```cpp
  template < class, class=void >
  struct is_iterator : std::false_type {};

  template < typename T >
  struct is_iterator<T,
    std::enable_if_t<
      std::is_base_of<std::input_iterator_tag, typename std::iterator_traits<T>::iterator_category>::value
   || std::is_base_of<std::output_iterator_tag, typename std::iterator_traits<T>::iterator_category>::value
    >
  > : std::true_type
  {};

```

###コンパイル時条件の論理演算 [ 追記（2017-2-23）]

C++17から、コンパイル時条件（メタ関数の真偽）を演算した結果を返してくれるメタ関数が登場！
AND演算する可変長テンプレート **conjunction**
OR演算する可変長テンプレート **disjunction**
NOT演算する **negation** 
の3種類がある

ナンデこんなにややこしい名前なのかと思われたかもしれないが、
C++では`and`, `or`, `not`は予約語であり、使えないのだ
もともとboost MPLにあるand_, or_, not_と同じ名前で提案されていたが、こうなってしまったのだ

conjunction, disjunction は渡したメタ関数の`value`をAND（OR）した結果を返してくれる可変長テンプレートになっている

negation は渡したメタ関数の`value`を否定した結果を返してくれる

```cpp
// 全ての条件がtrueなら結果がtrueとなる
constexpr bool result1 = std::conjunction_v<
    std::true_type,
    std::is_void<void>,
    std::is_same<int,int>
>;

// 条件を否定する
constexpr bool result2 = std::negation_v<std::true_type>; // false
```

これを使えば、上例の`is_range`を以下のように書ける
直接`disjunction`を継承して、条件を羅列すれば良い
かなり楽になってきた感じ

```cpp
template < typename T >
struct is_range
  : std::disjunction<
     detail::enable_std_begin_end<T>,
     detail::enable_adl_begin_end<T>>
{};
```

is_iterator も書き換えてみよう

```cpp
template < typename T >
struct is_iterator : std::disjunction<
      std::is_base_of<std::input_iterator_tag, typename std::iterator_traits<T>::iterator_category>,
      std::is_base_of<std::output_iterator_tag, typename std::iterator_traits<T>::iterator_category>>
{};
```

複数のコンパイル時条件から新しいコンパイル時条件をつくるならこれだなって感じですね

##C++14 Generic Lambdaパワーで汎用的 has_xxx [ 追記（2017-9-03）]

特定のメンバ名ではなく、あとからいくらでも名前を指定できたら便利ですよね。
実は、Boostにあるんだが、ちょっとちがった方法を紹介する。
言うまでもないが、マクロである。
まず、全貌は以下。

```cpp
#include <type_traits>
#include <utility>
namespace cranberries {
  struct protean_bool {
    constexpr operator std::true_type() const { return{}; }
    constexpr operator std::false_type() const { return{}; }
  };
  
  template <class T, class... Ts>
  struct Overload : T, Overload<Ts...> {
    Overload(T a, Ts... xs): T{a}, Overload<Ts...>{xs...} {}
    using T::operator();
    using Overload<Ts...>::operator();
  };

  template <class T> struct Overload<T> : T {
    Overload(T a): T{a} {}
    using T::operator();
  };

  template <class... F>
  inline constexpr Overload<F...>
  make_overload(F&&... f)
  {
    return {{std::forward<F>(f)}...};
  }
  template <class T> struct type_placeholder{ using type = T; };
}
#define CRANBERRIES_HAS_TYPE(XXX, ...) \
  bool(false \
  ? ::cranberries::make_overload( \
    [](auto x)->decltype(std::declval<typename decltype(x)::type::XXX>(), std::true_type{}) {return{}; }, \
    [](...)->std::false_type {return{}; } \
  )(::cranberries::type_placeholder<__VA_ARGS__>{}) \
    : ::cranberries::protean_bool{})
int main()
{
    static_assert(CRANBERRIES_HAS_TYPE(value_type, std::true_type),"");
}
```

これは、以下のように直接使える。

```cpp

static_assert(
  CRANBERRIES_HAS_TYPE(value_type, std::true_type), // true
  "");
```

仕組みを解説する。

コンパイル時に型特性を調べるにはSFINAEを使ってオーバーロードが成功するかを調べる。
まず、オーバーロードされた都合のいい関数をその場で作りたい。
そこで、以下のようなoperator()をusingしたクラスを用意する。
コンストラクタがないとgccに怒られる。

```cpp
  template <class T, class... Ts>
  struct Overload : T, Overload<Ts...> {
    Overload(T a, Ts... xs): T{a}, Overload<Ts...>{xs...} {}
    using T::operator();
    using Overload<Ts...>::operator();
  };

  template <class T> struct Overload<T> : T {
    Overload(T a): T{a} {}
    using T::operator();
  };
```

make_overload()に関数を渡すと、オーバーロードされた関数のように振る舞う。
これにジェネリックラムダを渡してその場でオーバーロードされた関数を作ってしまう。
以下のようにすると、引数から型をdecltype(x)で取得しXXXという型メンバがあるかを確かめられる。
`__VA_ARGS__`で展開しているのはテンプレートの場合クラスにカンマが使われる可能性があるため。
また、クラスがdefault constructibleかどうかも定かではないため、
type_placeholder<T>というdefault constructibleな型にクラスを埋め込みラムダ式に引数として渡して推論させる。
引数xをdecltypeして依存型名typeから目的の型を取り出す。

```cpp
cranberries::make_overload(
    [](auto x)->decltype(std::declval<typename decltype(x)::type::XXX>(), std::true_type{}) {return{}; },
    [](...)->std::false_type {return{}; }
  )(::cranberries::type_placeholder<__VA_ARGS__>{})
```

ただし、この方法は重大な欠点があって、ジェネリックラムダを使っているせいで未評価オペランドの文脈で使用できない。
decltypeで型を取得できないので以下ができない。

```cpp
// ラムダ式は未評価式で使えないのでこれはダメ！
decltype(
cranberries::make_overload(
    [](auto x)->decltype(std::declval<typename decltype(x)::type::XXX>(), std::true_type{}) {return{}; },
    [](...)->std::false_type {return{}; }
  )(::cranberries::type_placeholder<SOME_TYPE>())
)::value
```

decltypeが封じられたのでもう少し工夫する必要がある。
僕の大好きな条件演算子を使うことにする。
下準備としてprotean_boolクラスを作っておく（ポケモンを英語プレイしていたら分かるだろうが、proteanとは変幻自在という意味）。

```cpp
  struct protean_bool {
    constexpr operator std::true_type() const { return{}; }
    constexpr operator std::false_type() const { return{}; }
  };
```

次のように第3オペランドを常に戻り値とする条件演算子を書く。

```cpp
false ? make_overload(...)(x) : cranberries::protean_bool{};
```

この条件演算子の戻り値型はmake_overloadの呼び出しの結果によって変わる。

ラムダ式のdecltypeがメンバの引数xに対してdetectionに成功するとstd::true_typeを返すラムダ式がオーバーロードに成功し呼ばれる。
失敗すれば、std::false_typeを返すラムダ式がオーバーロードに成功し呼ばれる。
条件式はfalseなので、protean_boolが戻り値になる。
もし、make_overloadの戻り値型がstd::false_typeならprotean_boolはstd::false_typeに暗黙に変換される。
もし、make_overloadの戻り値型がstd::true_typeならprotean_boolはstd::true_typeに暗黙に変換される。
最終的にstd::true_typeかstd::false_typeのoperator bool()によってtrueかfalseに変換されbool値が手に入る。



##跋文
参考になりましたでしょうか？
メタ関数で有意義なTMPライフを満喫しましょう！
間違いや質問、ご意見等は
コメントしていただくか
[いなむ先生 | Twitter](https://twitter.com/_EnumHack)
までご一報ください
できるだけすみやかに対応させていただきます

---
__All text is available under the CC0 1.0 Universal license.__