『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属性を消去することであり、そのタイプメンバーtypevoidに指定され、継承されたベースクラス部分が__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_constantC++14/17では、ヘッダファイルが複数修正されています.このうち、重要なのは2つです.
  • C++14では、テンプレートにintegral_constantのリロードが追加する:
    constexpr value_type operator() const noexcept { return value; }
    
    これは、このテンプレートを関数オブジェクト(functional object)として渡すことができることを意味し、このような方法でタイプ判定の結果を得ることもできる:
    bool _v_flag = std::is_void<void>(); // true
    
  • .
  • C++17には、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をコンパイルコマンドに追加してこの問題
  • を解決することができる.