8
14

More than 5 years have passed since last update.

decltypeとtype traits

Last updated at Posted at 2014-12-31

みなさんあけましておめでとうございます。

あけましておめでとうございますって言われた時にありがとうって返すのはやめてあげてくださいw

前回はスフィネとかSTLについて書いていったわけですが、今日も深淵なC++のトピックを書いていきたいと思います。

decltype

http://qiita.com/rinse_/items/3c9030f4af33b6bbc53a
ここにすでに書かれていますが、式を引数にして型が返却されますC++11以降の機能ですが、要はこういう風に使います。

decltype1.cpp
#include <iostream>
using namespace std;
int main() {
    auto a = 10;
    //aの型によって違うベクトルを作りたい...
    vector<decltype(a)> v;
}
decltype2.cpp
#include <iostream>
using namespace std;

struct B {
    int b;
};

struct D {
   int d;
};

//返り値型がaに合わせて変化してほしい...
//Bも受け取れるし、Dの場合もある...
auto test(const B& a) -> decltype(a) {
    return a;
}
int main() {
    B b_instance;
    D d_instance;
    test(b_instance);
    test(d_instance);
}

この例だと普通に型引数入れれば良さそうなもんですが、動的に型が変わる場合を想定すると、このdecltypeは大変強力です。
autoの補助みたいな使い方をすることが大部分な気がしますが、型を返却するっていうのは結構面白いです。

type traits (型特性メタ関数)

type traitsとは型特性の事を言いますが、型特性メタ関数とは、型の特徴を調べたり、その特徴を操作する関数です。具体例を見てみると

has_iterator.cpp
#include <iostream>
#include <vector>
#include <type_traits>

using namespace std;

struct Has_iterator_impl {

    //型引数がイテレータを持っていればTrue型
    template <class T>
    static true_type check(typename T::iterator*);

    //それ以外ならFalse型
    template <class T>
    static false_type check(...);
};

template <class T>
class Has_iterator : public decltype(Has_iterator_impl::check<T>(nullptr)){};

int main() {
    static_assert(has_iterator<vector<int>>::value,
    "イテレータ持っています");

    static_assert(!has_iterator<int>::value,
    "イテレータ持っていません");
}

コードがめちゃめちゃ複雑な気がしますが、SFINAEとdecltypeを理解していればさほど難しい動作はしていません。ここで実はtype_traitsヘッダを使っていますが、ここには型特性における関数をいろいろ入っています。true_type, false_typeなどはその典型で、true_type::valueがtrue false_type::valueがfalseとなるメタクラスです。
次に、decltypeを見ると、ここにはvectorまたはintが渡されています。継承元のcheck関数には何も引数に渡しませんが、代わりに型引数を渡します。
そして、ここでSFINAEが起き、渡された型でtypename T::iterator*が宣言できる場合は宣言し、そうでなければそれ以外の宣言を行いますそれぞれの場合でtrue_typeを継承するのか、false_typeを継承するのかが決定されます。

これでできることはiteratorを持っていた場合はiteratorも用いてループ処理を行い、そうでない場合は通常のfor_eachで行う、繰り返し不能の場合はstaticにアサーションを出す

といったことができます。
さらに応用させると、特定の演算子がオーバーロードされているか、特定の機能が実装済みか否かなどを判定するメタ関数を作成することができます。

ここでのミソは以下の3点だと思います。

  • decltypeは型を返す関数で、継承宣言にも使える
  • SFINAEで置き換え失敗の処理は可変長引数...を用いる
  • type_traitsにはtrue_type, false_typeなど、メタ関数作成ツールがる

STLメタ関数

実は、ここまで書いたことはすでにSTLに実装済みです。ここではSTLに初めから組み込まれている特性メタ関数を紹介します。

type_traits.cpp
is_void       //型がvoidか否か判定する
is_empty      //要素が空かどうか判定する
is_class      //型がクラスか組み込みか判定する
is_abstract   //型が抽象クラスか否か判定する
is_assignable //+演算子が定義されているか判定する
enable_if     //可能なら...何かする

特にこの中のenable_ifは技巧的で、SFINAEの集大成と言えそうです。可能なら特定の処理を行い、不可能なら別の処理をする。というのが一発で実装できます。

他にもいろんなtype_traitsがあるのでメタプログラマは一通り目を通すといいかもしれません。

8
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
14