2017-07-31 3 views
0

SFINAEで実験していました。 my_functionのインスタンシエーションでmy_type_aを推測できないのはなぜですか?SFINAEとテンプレート関数のインスタンス化:SFINAE対応型の関数引数でテンプレート引数を使用できないのはなぜですか?

class my_type_a {}; 

template <typename T> 
class my_common_type { 
public: 
    constexpr static const bool valid = false; 
}; 

template <> 
class my_common_type<my_type_a> { 
public: 
    constexpr static const bool valid = true; 
    using type = my_type_a; 
}; 

template <typename T> using my_common_type_t = typename my_common_type<T>::type; 

template <typename T, typename V> 
void my_function(my_common_type_t<T> my_cvalue, V my_value) {} 

int main(void) { 
    my_function(my_type_a(), 1.0); 
} 

G ++は私にこれを与える:私が期待したもの

/home/flisboac/test-template-template-arg-subst.cpp: In function ‘int main()’: 
/home/flisboac/test-template-template-arg-subst.cpp:21:30: error: no matching function for call to ‘my_function(my_type_a, double)’ 
    my_function(my_type_a(), 1.0); 
          ^
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: candidate: template<class T, class V> void my_function(my_common_type_t<T>, V) 
void my_function(my_common_type_t<T> my_type, V my_value) {} 
     ^~~~~~~~~~~ 
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: template argument deduction/substitution failed: 
/home/flisboac/test-template-template-arg-subst.cpp:21:30: note: couldn't deduce template parameter ‘T’ 
    my_function(my_type_a(), 1.0); 
          ^

は、私がmainで行ったようにmy_functionを呼び出すときに、Tは、関数の最初の引数の型、およびその型に推測されるだろう、ということでした関数のインスタンス化で使用されます。しかし、

は、これを行うためのさまざまな方法があります... my_common_type_t<T>が機能する前にインスタンス化され、それでも、 my_cvalueの種類がとにかく my_type_aになるだろうので、これは動作しないでしょう、なぜ私が見ることができないようですか?私はちょうど2つ(またはそれ以上)のレベルのテンプレート間接参照を避けるべきですか?

+0

'my_common_type :: type'では、' T'は[推論されないコンテキスト](http://en.cppreference.com/w/cpp/language/template_argument_deduction#Non-deduced_contexts)にあります。あなたは 'my_common_type'を' my_type_a'と互換性があるように出てくることを期待して、 'my_common_type'を可能なすべての型' T'でインスタンス化することを期待しています。そのようなタイプを分析的に見つけようと試みる定理を証明する定理に従事している。コンパイラはどちらも実行しません。 –

+0

@Igor私は、あなたが提供したリンクからのルール1が私の例と必ず一致することを理解しています。しかし、なぜTが必ずしも 'my_type_a 'ではないのはなぜ分かりませんか? 'my_common_type 'が 'my_function'をインスタンス化する前にインスタンス化されていれば、型は' my_type_a'または何もない(したがって、関数はSFINAEを通して削除されます)。コンパイラは、その間またはその後にインスタンス化された場合、コンパイラは 'my_common_type 'の情報を候補として持つでしょう(そのため、 'T = my_type_a')、そうではありませんか? –

+0

*「なぜわかりませんか」*これは私が話していた定理を証明する運動です。ここで「クリア」とは、「利用可能な事実から証明できる」ことを意味します。おそらくそれは可能ですが、コンパイラはこの種の推論エンジンを持つ必要はありません。 –

答えて

2

さて、このことを考慮してください。コンパイラはmy_common_type<int>またはmy_common_type<double>を選択

template <> 
struct my_common_type<int> { 
    constexpr static const bool valid = true; 
    using type = my_type_a; 
}; 

template <> 
struct my_common_type<double> { 
    constexpr static const bool valid = true; 
    using type = my_type_a; 
}; 

// ... 

int main(void) { 
    my_function(my_type_a{}, 1.0); 
} 

していますか?

言語があなたの場合に控除を許可する場合は、Tmy_common_type<T>::typeになるように一致させる必要があります。これは、関数パラメータに送信する正確なタイプを得るためです。明らかに、それは不可能ではありませんが、上記の私の例では、複数の選択肢があるかもしれません!

幸いにも、my_common_type<T>は常にTになることをコンパイラに伝える方法があります。このトリックの基本は次のとおりです。

template<typename T> 
using test_t = T; 

template<typename T> 
void call(test_t<T>) {} 

int main() { 
    call(1); 
} 

Tとは何ですか? int、簡単!コンパイラはこの種のマッチングに満足しています。また、test_tは特殊化することができないので、test_t<soxething>somethingであることがわかっています。

また、これは別名の複数のレベルで、あまりにも取り組んでいる。

template<typename T> 
using test_t = T; 

template<typename T> 
using test2_t = test_t<T>; 

template<typename T> 
void call(test2_t<T>) {} 

int main() { 
    call(1); // will also work 
} 

私たちは、あなたの場合にこれを適用することができますが、我々はいくつかのツールが必要になります。

template<typename T, typename...> 
using first_t = T; 

これは簡単に同じです前述のように一致しますが、使用されない引数を送信することもできます。この未使用パックにはsfinaeを作ります。これも機能していることを

template <typename T> 
using my_common_type_t = first_t<T, typename my_common_type<T>::type>; 

注:

template <typename T> 
using my_common_type_t = first_t<T, std::enable_if_t<my_common_type<T>::valid>>; 

今控除のように起こるのだろう

さて、未使用のパックに制約を加えることながら、やはり簡単に一致するmy_common_type_tを書き換えます期待!Live (GCC)Live (Clang)

この場合sfinaeは、(パラメータを落とし)のみC++ 14が発生することが保証されているように、このトリックのみ、C++ 14で動作することに注意してください。

特性にstructを使用するか、public:を使用してメンバーmy_common_type<T>::typeをパブリックにするか、GCCが偽のエラーを出力するように注意してください。

+0

うわー、それは確かに良い選択肢であり、それは私の質問に答える!しかし、私の場合は、テンプレートパラメータによって 'my_common_type :: type'が選択されているので、もっと複雑です。言い換えれば、それは有効化の問題だけではありません。また、私はコンパイラがまず関数呼び出しサイトを見て、 'my_common_type'の特殊化にかかわらず、' T'は 'my_type_a'であると考えました。また、 'T_ my_type_a'の' my_common_type 'の特殊化されていないので、' my_common_type 'も' my_common_type 'も選択されていないと思います。 –

+1

私は自分の質問を更新したいと思っていましたが、あなたの答えはとても役に立ち、私の特定の問題に答えるために別の質問をする気がします。 –

+0

@FlávioLisbôaありがとう!常に喜んで助けてください。別の質問をすることは、実際にこのサイトでそれを行う適切な方法でしょう。 –

関連する問題