5

Stroustroupの本「C++プログラミング第4版」を検討しています。そして私は彼の例をマトリックスデザインに従おうとしています。複雑なタイプ名パラメータを持つテンプレート関数を理解する助けが必要です

彼のマトリックスクラスはテンプレートに大きく依存しており、私はそれらを理解するために最善を尽くしています。 ここ

A Matrix_slice要素の位置に添字の セットをマッピングマトリックスの実装の一部であるこのマトリックスのヘルパークラスの一つです。以前の本の中で

template<typename... Dims, 
     typename = Enable_if<All(Convertible<Dims,size_t>()...)>> 
     size_t operator()(Dims... dims) const; // calculate index from a set of subscripts 

:ここ

template<size_t N> 
struct Matrix_slice { 
    Matrix_slice() = default; // an empty matrix: no elements 
    Matrix_slice(size_t s, initializer_list<size_t> exts); // extents 
    Matrix_slice(size_t s, initializer_list<size_t> exts, initializer_list<siz e_t> strs);// extents and strides 
    template<typename... Dims> // N extents 
    Matrix_slice(Dims... dims); 


    template<typename... Dims, 
    typename = Enable_if<All(Convertible<Dims,size_t>()...)>> 
    size_t operator()(Dims... dims) const; // calculate index from a set of subscripts 

    size_t size; // total number of elements 
    size_t start; // star ting offset 
    array<size_t,N> extents; // number of elements in each dimension 
    array<size_t,N> strides; // offsets between elements in each dimension 
}; 
I 

は私の質問の主題を構築する線です:それは一般のスライスのアイデア (§40.5.6)を使用していますEnable_ifとAll()がどのように実装されているのかを説明しています。

template<bool B,typename T> 
using Enable_if = typename std::enable_if<B, T>::type; 


constexpr bool All(){ 
    return true; 
} 
template<typename...Args> 
constexpr bool All(bool b, Args... args) 
{ 
    return b && All(args...); 
} 

彼らはすでに仕事と彼のEnable_ifの実装を見て、私もコンバーチブルの機能を推定する方法を理解するための形成:

template<typename From,typename To> 
bool Convertible(){ 
    //I think that it looks like that, but I haven't found 
    //this one in the book, so I might be wrong 
    return std::is_convertible<From, To>::value; 
} 

だから、私はこのテンプレート関数宣言 のビルディングブロックをundersandことができますが、しようとしたとき、私は混乱しています彼らがどのようにaltogatherを働かせるか理解する。 私はあなたが私たちが最後にものを手に入れるんので、

template<typename... Dims, 
//so here we accept the fact that we can have multiple arguments like (1,2,3,4) 

     typename = Enable_if<All(Convertible<Dims,size_t>()...)>> 
     //Evaluating and expanding from inside out my guess will be 
     //for example if Dims = 1,2,3,4,5 
     //Convertible<Dims,size_t>()... = Convertible<1,2,3,4,5,size_t>() = 
     //= Convertible<typeof(1),size_t>(),Convertible<typeof(2),size_t>(),Convertible<typeof(3),size_t>(),... 
     //= true,true,true,true,true 

     //All() is thus expanded to All(true,true,true,true,true)    
     //=true; 

     //Enable_if<true> 
     //here is point of confusion. Enable_if takes two tamplate arguments, 
     //Enable_if<bool B,typename T> 
     //but here it only takes bool 

     //typename = Enable_if(...) this one is also confusing 

     size_t operator()(Dims... dims) const; // calculate index from a set of subscripts 

を助けることができることを願っていますか? この構築

template<typename ...Dims,typename = Enable_if<true>> 
size_t operator()(Dims... dims) const; 

質問は以下のとおりです。

  1. は、我々は
  2. はなぜ我々は型名
  3. のために( '=')が割り当てられていないEnable_ifための第二のテンプレート引数を必要としないでください私たちは最終的に何を得ますか?

更新: あなたはstd::enable_ifがそのテンプレートで、私はここに The C++ Programming Language 4th editionページ841で参照するよ同じ本(マトリックスデザイン)

+0

これは実際のコードにすることはできません。 'Enable_if ()...)>'は、テンプレート引数がコンパイル時に知られている式でしかないので、コンパイラエラーを引き起こします。 'All'の戻り値は返しません。 – SergeyA

+0

@SergeyA彼は関数定義の前に 'constexpr'が足りないと思う。私はそれがうまくいくと確信しています。 – SirGuy

+0

@GuyGreer、OPかもしれません。しかしOP以外は何が欠けているのですか? ;) – SergeyA

答えて

1

行方不明constexprのにもかかわらずに、コードを確認することができます2つのパラメータを取りますが、2番目のパラメータはデフォルトでvoidになります。その慣習を維持するために、これに素早いエイリアスを書くときは意味があります。

したがって、エイリアスのように定義する必要があります。私はこのデフォルトのパラメータは、これがその問題を修正するだけという、ブック内に存在するか否かに何の洞察力を持っていない

template <bool b, class T = void> 
    using Enable_if = typename std::enable_if<b, T>::type; 

タイプの割り当てはtype aliasと呼ばれ、スズについて言えば、別名を参照すると実際に別名を参照しています。この場合は、Enable_if<b>と書くと、コンパイラは手軽にそれをtypename std::enable_if<b, void>::typeに展開し、余計な入力を省くことができます。

最終的には、渡されたすべてのパラメータがstd::size_tに変換可能な場合にのみ呼び出し可能な関数です。これにより、特定の条件が満たされない場合に関数のオーバーロードを無視することができます。これは、どの関数を呼び出すかを選択するためだけに型を一致させるより強力な手法です。 std::enable_ifのリンクにはなぜそれをしたいのかという詳細な情報がありますが、初心者にはこの件名が頭におかしいと警告しています。

+0

ありがとうまだありませんそれはトピックで2つの質問より多く答えていません – Antiusninja

4

これは基本的なSFINAEです。例えば、hereを読むことができます。回答については

は、私はここの代わりに本の中で与えられた EnableIfstd::enable_if_tを使用していますが、二つは同じです:@GuyGreer、の2番目のテンプレートパラメータでの回答で述べたように

  1. デフォルトはvoidです。

  2. コードテンプレート定義=

    template<typename ...Dims, typename some_unused_type = enable_if_t<true> > 
    size_t operator()(Dims... dims) const; 
    

    「正常」関数として読み取ることができ、パラメータsome_unused_typeは右側にタイプにデフォルト設定されています。また、タイプsome_unused_typeを明示的に使用しないため、名前を付ける必要もなく、単に空白のままにすることもできます。

    これは、関数パラメータでも見られるC++の通常の方法です。たとえば、operator++(int)を確認してください.1人はoperator++(int i)などを書きません。

  3. 何が起こっているのかは、の省略形であるSFINAEです。代替失敗はエラーではありません。ここでは2例があります:std::enable_if_tのブール引数がfalseある場合typename =のRHSに有効なタイプが存在しないため

    • まず、一つは

      template<typename ...Dims, typename = /* not a type */> 
      size_t operator()(Dims ... dims) const; 
      

      を取得し、型推論は失敗します。ただし、SFINAEのため、コンパイル時エラーにはなりませんが、オーバーロードセットから関数を削除することになります。

      実際の結果は、あたかも関数が定義されていないかのようです。

    • std::enable_if_tのブール引数がtrueであれば第二には、一つはtypename = void

      template<typename ...Dims, typename = void> 
      size_t operator()(Dims... dims) const; 
      

      を取得するには、有効な型定義であるので、機能を削除する必要はありません。したがって、通常使用することができます。あなたの例に適用

template<typename... Dims, 
     typename = Enable_if<All(Convertible<Dims,size_t>()...)>> 
     size_t operator()(Dims... dims) const; 

上記はAll(Convertible<Dims,size_t>()...trueある場合にのみ、この機能が存在することを意味します。これは、基本的に、関数のパラメータはすべて整数のインデックスでなければならないことを意味します(私は個人的には、std::is_integral<T>の点でそれを書いています)。

+0

私はあなたが好きなら私の答えをニックピツプすることも自由に感じます:P)、タイプ控除の失敗_can_はコンパイルエラーにつながります代わりに別の関数のオーバーロードが見つかった場合には、それはできません。 – SirGuy

+0

また、 'convertible'は' std :: is_integral'よりも一般的です。後者は、型のあらかじめ決められたリストからオカレンスをチェックしますが、前者は 'std :: size_t '(私はかなりの整数型への変換で十分であると確信しています。その後、型変換は' std :: size_t'を得るための変換ではなく)。 – SirGuy

+0

これは大きな説明です。どうもありがとう。 – Antiusninja

関連する問題