2015年5月19日火曜日

decltype と type traits (C++11)

decltype


decltype(expression) で式の型を得ることができる。

利用シーン


  • プロトタイプ宣言の型指定
  • 関数の戻り値の型を得る
typeinfoを併用して使い方を見てみる。typeinfoは実行時型情報(RTTI RunTimeTypeIdentification)を取得する機能である。

また、C++のコンパイラが名前マングルした(シンボルがuniqueな名前となるように)文字列を閲覧するのではなく、デマングルして宣言した型であることを確認する。

libstdc++cxxapi abi::__cxa_demangle()関数を利用する。

//
// decltype.cpp
// CplusplusPractice
//
// Created by masai on 2015/04/10.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
#include <vector>
#include <typeinfo> //オブジェクトの型を知るためのライブラリ
#include <cxxabi.h>
using namespace std;
int methoz(int i);
double methoz(double d);
struct B {
virtual ~B(){}
};
struct D : B {
};
//戻り値型が引数aに合わせて変化させたい
//Bの子であるDの場合も存在する
auto test(const B& a) -> decltype(a) {
cout << "typeid(a)=" << abi::__cxa_demangle(typeid(a).name(), 0, 0, 0) << endl;
return a;
}
int main(int argc, const char * argv[]) {
// methoz(1)の戻り値の型をもつ変数nを定義する
decltype(methoz(1)) n = 3.4;
// int型として出力される
cout << n << endl;
decltype(methoz(2.3)) m = 3.4;
// double型として出力される
cout << m << endl;
// 型によって異なるvectorをつくる
auto a = 10;
vector<decltype(a)> vec;
// int型として挿入される
vec.push_back(3.3);
vec.push_back(10);
for(int i = 0; i < vec.size(); ++i){
cout << vec[i] << endl;
}
// 引数に合わせて戻り値が変化する関数
B b_instance;
D d_instance;
cout << abi::__cxa_demangle(typeid(test).name(), 0, 0, 0) << endl;
test(b_instance);
test(d_instance);
return 0;
}
view raw decltype.cpp hosted with ❤ by GitHub

type traits

型の特徴を調べたり、型の特徴を操作する関数
C++11では、type_traitsをインポートして enable_if が使える。
template <bool B, Class T = void>
struct enable_if;
enable_ifは、Bがtrueの時にtypedef T type;をもち、Bがfalseであればtypeをもたない。

SFINAE(Substitution Failure Is Not An Error)用途に使われる。
SFINAEは置き換え失敗はエラーにあらず、という意味で関数をオーバーライドする際、型変換がうまくいかなければオーバーライドの候補から自動ではずすことができる記述である。

ていうかいろんな書き方があってC++レベルが足りてない。以下の様な書き方があるってことを認識しておこう。

//
// typetraits.cpp
// CplusplusPractice
//
// Created by masai on 2015/05/19.
// Copyright (c) 2015年 masai. All rights reserved.
//
#include <iostream>
#include <type_traits>
// static_assertでコンパル時チェックを行うことが可能
// enable_ifにtrueのみ渡すと、デフォルトのvoidの型になる
static_assert(std::is_same<std::enable_if<true>::type, void>::value, "::type");
// enable_ifにtrueとintを渡すと、int型を返す
static_assert(std::is_same<std::enable_if<true, int>::type, int>::value, "::type");
// enable_ifにtrueとunsignedを渡すと、unsigned型を返す
static_assert(std::is_same<std::enable_if<true, unsigned>::type, unsigned>::value, "::type");
// enable_ifの第一引数にfalseを渡すと、type は無くなる
// **********************************************************************************
// is_integralはTがint型かどうかを確認する
// 以下の記述方法はTがintでない場合には、funcの呼び出しをエラーとする
template<typename T>
void func(T x, typename std::enable_if<std::is_integral<T>::value>::type* =0){
std::cout << "integral:" << x << std::endl;
}
// std::disable_if はないので、std::enable_if を使用する
template<typename T>
void func(T x, typename std::enable_if<!std::is_integral<T>::value>::type* =0){
std::cout << "other:" << x << std::endl;
}
// **********************************************************************************
// 以下は仮引数にenable_ifを記述しないタイプの書き方(enablerの利用)
// 条件無し。T は何型でもOKな関数f
template<class T>
void f(T const &){}
// T がint型と等しい時のみ有効な関数g
// is_integralはis_same<T, int>でも良い
extern void* enabler; // enabler
template<class T, typename std::enable_if<std::is_integral<T>::value>::type*& = enabler>
void g(T const &){
}
// **********************************************************************************
int main(int argc, const char * argv[]) {
// int型の変数a
int a = 0;
double d = 2.3;
// 構造体s
struct s{} b;
// func
func(a);
func(d);
f(a); // OK
f(b); // OK
g(a); // OK
//g(b); // gはint以外はエラーになる
return 0;
}
view raw typetraits.cpp hosted with ❤ by GitHub

0 件のコメント:

コメントを投稿