2011-08-19 16 views
31

どのようにそれがのparamaterパックの内容をプリントアウトする再帰可変長引数テンプレートを作成することは可能でしょうか?私はこれをしようとしていますが、それはコンパイルに失敗し :再帰可変引数テンプレートは、パラメータパックの内容を印刷する

template <typename First, typename ...Args> 
std::string type_name() { 
    return std::string(typeid(First).name()) + " " + type_name<Args...>(); 
} 
std::string type_name() { 
    return ""; 
} 

にはどうすれば再帰を終了しなければなら?

答えて

30

あなたは再帰を終了する部分特殊化を使用する必要がありますが、部分的にC++での無料の機能を特化することができないので、あなたは、静的メンバ関数で実装クラスを作成する必要があります。

template <typename... Args> 
struct Impl; 

template <typename First, typename... Args> 
struct Impl<First, Args...> 
{ 
    static std::string name() 
    { 
    return std::string(typeid(First).name()) + " " + Impl<Args...>::name(); 
    } 
}; 

template <> 
struct Impl<> 
{ 
    static std::string name() 
    { 
    return ""; 
    } 
}; 

template <typename... Args> 
std::string type_name() 
{ 
    return Impl<Args...>::name(); 
} 

int main() 
{ 
    std::cout << type_name<int, bool, char, double>() << std::endl; // "i b c d" 
    return 0; 
} 

Implの最初の宣言はちょうどa workaround for a shortcoming in g++ 4.6(以下)であること。バリデーショナルテンプレートを正しく実装すると、これは必要ありません。

Check it out in action at ideone.com

+0

あなたが引用回避策リンクが死んでいます。あなたはバグのないg ++なしでこれを行うための正しい方法を精緻化できますか? – cdhowie

+0

この答えが正しいですが、これの回避策は、Mac – Gerard

+0

にクランのためにも必要である、それは時代遅れです。より簡単なアプローチが可能です。他の回答を参照してください。 – Johannes

13

機能のために存在しない部分特殊化する代わりに、あなたはtypifierクラスにオーバーロードを使用することができます。

#include <string> 
#include <iostream> 
#include <typeinfo> 

template <unsigned int N> struct NumberToType { }; 

template <typename T> 
std::string my_type_name(NumberToType<0> = NumberToType<0>()) 
{ 
    return std::string(typeid(T).name()); 
} 

template <typename T, typename ...Args> 
std::string my_type_name(NumberToType<sizeof...(Args)> = NumberToType<sizeof...(Args)>()) 
{ 
    return std::string(typeid(T).name()) + " " + my_type_name<Args...>(NumberToType<sizeof...(Args)-1>()); 
} 

int main() 
{ 
    std::cout << my_type_name<int, double, char>() << std::endl; 
} 
29

再帰を終了するには非常にエレガントな方法は実際にあります:

template <typename Last> 
std::string type_name() { 
    return std::string(typeid(Last).name()); 
} 

template <typename First, typename Second, typename ...Rest> 
std::string type_name() { 
    return std::string(typeid(First).name()) + " " + type_name<Second, Rest...>(); 
} 

私は最初にtemplate <typename Last>template <typename First, typename ...Rest>を試しましたが、それはあいまいであると考えられました(残りはゼロ要素)。あなたも行うことができ、コードの重複のビットを避けるためにCompilation Error on Recursive Variadic Template Function


注::

代替として
template <typename Last> 
std::string type_name() { 
    return std::string(typeid(Last).name()); 
} 

template <typename First, typename Second, typename ...Rest> 
std::string type_name() { 
    return type_name<First>() + " " + type_name<Second, Rest...>(); 
} 
+4

これは受け入れられる回答でなければなりません。 –

+0

パラメータパックが空の場合、これは機能しませんが、それ以外の場合は非常に良い解決策です。 – zennehoy

+1

はちょうど空の場合(あなたは1が必要な場合は) – Mordachai

4

、あなたは内のパラメータパックを解凍することができますこの質問は、私に決定的な解決策を示しました。次の例のように置き換えます。

#include<string> 
#include<iostream> 
#include<typeinfo> 

template <typename T, typename ...Args> 
std::string type_name() { 
    std::string str = typeid(T).name(); 
    int arr[] = { 0, (str += std::string{" "} + typeid(Args).name(), 0)... }; 
    (void)arr; 
    return str; 
} 

int main() { 
    auto str = type_name<int, double, char>(); 
    std::cout << str << std::endl; 
} 

実際に再帰を行う必要はありません。

+0

コンパイルエラーに起因 'constexpr'関数の宣言(VS2015アップデート3)のための第三の過負荷を追加します。それは –

+0

@skypkackを削除する場合は、すべてのは、コンパイルされます、私は '/ STDを使用してコンパイル:C++ latest'、すなわち、C++ 17''サポートしています。エラーは以下のとおりです。 'エラー\t C3250 \t 'STR':宣言は 'constexprの' 関数本体\t'と 'エラー\t C3250 \tで許可されていません 'ARR':宣言は\t' –

+0

'constexprの' 関数本体では使用できません@IvanKushコードが更新されました。ありがとうございました。 – skypjack

関連する問題