『C++Primer Plus 6 th.ed』読書ノートその4:簡単なtype_traits実装とその応用
9484 ワード
について
は、2011年の標準からSTLに追加されたC++のヘッダファイルです.名前の通り、テンプレートクラスのタイプを決定する一連の属性を提供します.たとえば、次のようになります.// void
template<typename _Tp>
struct is_void : public __is_void_helper<typename remove_cv<_Tp>::type>::type
{ };
//
#include
bool _v_flag = std::is_void<int>::value; // false
bool _i_flag = std::is_void<void>::value; // true
より具体的な例は、ここでは、
is_void<>
を例に、
のc++組み込みタイプの実装方法を簡単に分析します.では、なぜカスタムタイプの実装について話さないのでしょうか.C++はコンパイル時に多くのメタ情報を生成できないため、テンプレートの置換と展開ができない.このコードの実現はコンパイラのソースコードに直接書かれているが、私は怠け者で、gccのソースコードをひっくり返したくない......is_void<>
テンプレート展開まず、
is_void<>
はテンプレートクラスですが、このテンプレートクラスはstruct
によって定義されています.メンバーアクセス権のデフォルトがpublic
であることを除いて、class
と同じです.ソースコードは文章の冒頭に貼り付けられています.ここでもう一度貼ります.template<typename _Tp>
struct is_void : public __is_void_helper<typename remove_cv<_Tp>::type>::type
{ };
別のテンプレートクラス
__is_void_helper
が公開的に継承されていることがわかります.このクラステンプレートは私たちが次に探している目標であるようですが、この継承の他の部分を見てから、このベースクラスの実装を考えてみましょう.remove_cv
もファイル内のクラステンプレートとして宣言されていますが、このファイルには宣言のみで実現されていません.おそらくコンパイラによって実現されるだろう.その役割は、例えば、入力タイプパラメータがconst/volatile
である入力タイプのconst void
属性を消去することであり、そのタイプメンバーtype
がvoid
に指定され、継承されたベースクラス部分が__is_void_helper::type
になる.template<typename>
struct __is_void_helper : public false_type { };
template<>
struct __is_void_helper<void> : public true_type { }
ここで、入力タイプによって、
__is_void_helper<>
は2つの異なる継承ルートを有していることが分かる.入力__is_void_helper<>
はvoid
を継承し、そうでなければtrue_type
を継承し、次に、この2つの異なるベースクラスの実装を引き続き見る./// The type used as a compile-time boolean with true value.
typedef integral_constant<bool, true> true_type;
/// The type used as a compile-time boolean with false value.
typedef integral_constant<bool, false> false_type;
2つのベースクラスが同じタイプの異なるパラメータのタイプを再定義していることは明らかです.この
false_type
を見つけることが重要です.template<typename _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
typedef _Tp value_type;
typedef integral_constant<_Tp, __v> type;
constexpr operator value_type() const noexcept { return value; }
}
この時点では一目瞭然ですが、
intergral_constant
から入力されるタイプパラメータによって、最後に継承されるis_void<>
も異なり、テンプレート全体の展開過程は大まかに下図のようになっています.void
non-void
is_void<>
__is_void_helper< void >
__is_void_helper< typename T >
integral_constant< bool, true >
is_void< void >::value = true
integral_constant< bool, false >
is_void< void >::value = false
新しい規格の
integral_constant
C++14/17では、ヘッダファイルが複数修正されています.このうち、重要なのは2つです.
テンプレートにintegral_constant
のリロードが追加する:constexpr value_type operator() const noexcept { return value; }
これは、このテンプレートを関数オブジェクト(functional object)として渡すことができることを意味し、このような方法でタイプ判定の結果を得ることもできる:bool _v_flag = std::is_void<void>(); // true
operator()
を直接取得するテンプレート変数が追加されている.このような汎用ネーミング方式integral_constant::value
は、上記の例に対して、タイプ判定の結果を得ることができる:bool _v_flag = std::is_void_v<void>; // true
であるが、注意しなければならない.現在主流のコンパイラ(GCC/MSVC)でデフォルトで使用されているC++標準はC++14のようでコンパイルに失敗し、GCCに対してis_type_v<>
または--std=c++17
をコンパイルコマンドに追加してこの問題