5

編集:私はSFINAEを使用する際に簡単な間違いを犯しました。コンパイルエラーが解決されたので、私は以下のように言います。しかし、私はまだについて興味があります。の場合、この場合テンプレートパラメータを推論できません。なぜこのテンプレートパラメータは推論できませんか?

std::integer_sequenceの最大公約数(GCD)を計算するためのC++ 14テンプレートメタプログラムを作成したいと考えました。私はちょうど整数列の最初の2つの要素をはがし、そして-書き込まれるGCD_pairメタ関数を使用してそれらのGCDを見つける

template <typename T, T A, T B, T... Ints> 
struct GCD<std::integer_sequence<T, A, B, Ints...>> : 
     GCD<typename std::integer_sequence<T, GCD_pair<T, A, B>::value, Ints...>> {}; 

template <class T, T A, T B> 
struct GCD<std::integer_sequence<T, A, B>> : 
     GCD_pair<T, A, B> {}; 

int main() {  
    using seq = std::integer_sequence<int, 65537, 5, 10>; 
    cout << GCD<seq>::value << endl; 
    return 0; 
} 

:いくつかの工夫の後、私はこのほとんど完全な例を思い付きました。次に、GCDGCD_pairの結果と残りの要素に適用します。 GCD_pair

"明白な" 実装はコンパイルされません:

// This doesn't work either: 
// template parameters not deducible in partial specialization 
template <typename T, T M, T N, typename = void> 
struct GCD_pair : std::integral_constant<T, M> {}; 

template <typename T, T M, T N, typename std::enable_if<(M % N != 0)>::type> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

編集:

// This does not work: 
// type 'T' of template argument '0' depends on a template parameter 
template <typename T, T M, T N> 
struct GCD_pair : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

template <typename T, T M> 
struct GCD_pair<T, M, 0> : std::integral_constant<T, M> {}; 

だから私はSFINAEを使用して、別の可能な実装を試してみました私は、エラーをしました。 SFINAEの私の使用を修正する下記の答えを見てください。しかし、私はまだ質問に興味があります:

なぜテンプレートパラメータtypename std::enable_if<(M % N != 0)>::typeは推論できませんか?原則的にと推測できないか、この場合のようなパラメータが実際にはであると推測できますか?言い換えれば、これはコンパイラの実装監視と見なすことができますか?

私はboolテンプレートパラメータで条件(M % N != 0)を本質的に「隠す」ことによって、a slightly different versionを実装することができました。しかし、0operator!=との比較は、すべてのC++の整数型で完全に定義されているため、上記の両方が妥当な実装であると思います。

答えて

5

それは次のようになります。

template <typename T, T M, T N> 
struct GCD_pair<T, M, N, typename std::enable_if<(M % N != 0)>::type> 
    : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

あなたがC++ 14を使用しているので、あなたもstd::enable_if_tを使用して、それを簡素化することができます。

どちらの場合も
template <typename T, T M, T N> 
struct GCD_pair<T, M, N, std::enable_if_t<(M % N != 0)>> 
    : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

、条件(M % N != 0)が適用される場合、プライマリテンプレートと特殊化の両方がインスタンス化されると有効になりますが、特殊化はより専門化され、したがって選択されます。
条件が適用されない場合、sfinaeルールのために特殊化は黙って破棄されますが、プライマリテンプレートは依然として有効なので選択されます。

typetypename std::enable_if<(M % N != 0)>::type)が条件が真である場合、voidであることに注意してください。そのための
、あなたはパラメータのリストをテンプレート理論的には次のようになります。

template <typename T, T M, T N, void> 
struct GCD_pair<T, M, N, void>: std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

しかし、voidは、非型テンプレートパラメータとして許可されていません。部分特殊のテンプレート引数は、実際のテンプレート引数リスト

から推定することができるならば部分的な特殊化は、与えられた実際のテンプレート引数リストと一致する

:ことを私たちは持っている標準から最後

、また

部分特殊のテンプレート引数は、そのtemplaの構造を推定することができない場合te-parameter-listおよびtemplate-idの場合、プログラムは不正です。

あなたのケースでは、特殊化の4番目のパラメータは明白な理由から推論できません。たとえそれが有効であったとしても(そうでないと、前述のようにvoidになります)、実際のタイプまたは値を持たないタイプまたは非タイプのパラメータが得られます。
では、次の専門を持っていると仮定します。

template <typename T, T M, T N, int> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

コンパイラは、最後のテンプレートパラメータの値を推測することができますか?
これはあなたのケースではそれほど難しくありません。 おそらく、std::enable_ifの条件が有効な場合、パラメータリストが不正であるという事実を検出するはずです。いずれにしてもエラーであり、いずれかがコンパイルフェーズから外れている場合です。

template <typename T, T M, T N, typename = std::enable_if_t<(M % N != 0)>> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

またはこの:

template <typename T, T M, T N, std::enable_if_t<(M % N != 0)>* = nullptr> 
struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

とにかく、それらの両方が部分的に専門で許可されていないデフォルト引数は無効ですあなたが恩恵を受ける何

はこのようなものです。

+0

さらに、「GCD」は、1次テンプレートを必要とする。 'テンプレート構造体GCD;'。 – Corristo

+0

確かに!愚かな私、私は簡単な間違いをした。 – JohnDuck

+0

私はSFINAEの使用に間違いがあることを指摘したので、すぐにこれを受け入れました。テンプレートパラメータを推測できないのはなぜですか? – JohnDuck

関連する問題