2016-04-13 14 views
2

誰かが奇妙な出力をここで説明できますか?テンプレート関数からの奇妙な出力の説明

#include <iostream> 
#include <type_traits> 

template <typename T> 
constexpr auto has_foo_impl(int) -> decltype(typename T::foo{}, std::true_type{}); 

template <typename T> 
constexpr auto has_foo_impl(long) -> std::false_type; 

template <typename T> 
constexpr bool has_foo() { return decltype(has_foo_impl<T>(0))::value; } 

template <typename...> struct rank; 

template <typename First, typename... Rest> 
struct rank<First, Rest...> : rank<Rest...> {}; 

template <> struct rank<> {}; 

template <typename T, typename... Rest, typename... Args> 
auto check (rank<T, Rest...>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))> { 
    return T(args...); 
} 

//template <typename T, typename U, typename... Args> 
//auto check (rank<T,U>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))> { 
// return T(args...); 
//} 
// 
//template <typename T, typename... Args> 
//auto check (rank<T>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))> { 
// return T(args...); 
//} 

template <typename... Args> 
auto check (rank<>, Args...) { std::cout << "Nothing found.\n"; } 

template <typename... Ts> 
struct Factory { 
    template <typename... Args> 
    decltype(auto) create (Args... args) const { 
     return check(rank<Ts...>{}, args...); 
    } 
}; 

struct Object {}; 

struct Thing {}; 

struct Blob { 
    using foo = int; 
    Blob (int, double) { print(); } 
    void print() const { std::cout << "Blob\n"; } 
}; 

int main() { 
    Factory<Blob, Object, Thing>().create(4,3.5); // Blob 
    Factory<Object, Blob, Thing>().create(4,3.5); // Nothing found 
    Factory<Object, Thing, Blob>().create(4,3.5); // Nothing found 
} 

私はBlobが3回出力されることを期待しています。コメントアウトされたオーバーロードのコメントを外すときcheck、私はそれを得る。単一の変数checkは私がコメントアウトしたものを世話するべきではありませんか?結局、rank<First, Rest...>rank<Rest...>に由来します。

私は同じ仕事をするための他の方法を知っていますが、なぜこのランキング方法がここでは機能しないのだろうと思います。 Nothing foundが出力された場合、rank<>が渡されたことを意味します。中間ランクも渡されたことを意味します。

答えて

4

あなたはチェリーで1回の噛み付きを得るだけです。

SFINAEが推測されたオーバーロードを削除し、テンプレート引数を推測する他の(あまり推奨されない)方法がある場合でも、使用可能な各関数テンプレートはテンプレート引数を1回だけ取得します。

ので、与えられた:最初の引数の型としてrank<Object, Blob, Thing>

template <typename T, typename... Rest, typename... Args> 
auto check (rank<T, Rest...>, Args... args) -> std::enable_if_t<has_foo<T>(), decltype(T(args...))>; 

は、TObject[Blob, Thing]などRestと推定されます。それ以降、SFINAEは推測された過負荷を取り除きます。

あなたのコメントアウトされたオーバーロードのコメントを外し事実は、それが仕事になることは、それは、第一、第二、最後と最後の位置にBlobと協力すること、3つの関数テンプレートを与えるという理由だけで、純粋な偶然の一致です。 3つの引数でテストしました。これは、4(Example.

の2位に BlobObject, Blob, Thing, Whateverのために動作しません

また、すべてでは少し異なるの継承に対するテンプレート控除をランク付けするように見える打ち鳴らす、では動作しませんあなたのコメントアウトされたオーバーロードのコメントを外します。 (Example.

テンプレート引数の控除の機会を増やす必要があります。一方向は再帰(Example)です:

template <typename T, typename... Rest, typename... Args> 
auto check (rank<T, Rest...>, Args... args) -> std::enable_if_t<!has_foo<T>(), decltype(check(rank<Rest...>{}, args...))> { 
    return check(rank<Rest...>{}, args...); 
} 
+0

@ ecatmur非常に良い答えをありがとう。あなたは非常に不思議なように答えるこのフォローアップの質問に興味があるかもしれません:http://stackoverflow.com/questions/36611105/why-template-instantiations-go-on-forever-here – prestokeys

関連する問題