2011-08-19 13 views
31

Come è possibile creare un modello variadic ricorsivo per stampare il contenuto di un pacchetto di parametri? sto cercando con questo, ma non riesce a compilare:modello variadic ricorsivo per stampare il contenuto di un pacchetto di parametri

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

Come devo terminare la ricorsione?

risposta

30

È necessario utilizzare la specializzazione parziale per terminare la ricorsione, ma poiché non è possibile specializzare parzialmente le funzioni libere in C++, è necessario creare una classe di implementazione con una funzione membro statica.

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; 
} 

Quella prima dichiarazione di Impl è solo a workaround for a shortcoming in g++ 4.6 (e sotto). Non sarà necessario una volta implementato correttamente i modelli variadici.

Check it out in action at ideone.com

+0

Il collegamento temporaneo che citi è morto. Puoi elaborare il modo corretto per farlo senza un g ++ bacato? – cdhowie

+0

Questa soluzione alternativa è necessaria anche per Clang su Mac – Gerard

+0

Sebbene questa risposta sia corretta, non è aggiornata. È possibile un approccio più semplice, vedere altre risposte. – Johannes

13

In alternativa alla specializzazione parziale inesistente per le funzioni, è possibile utilizzare un sovraccarico su una classe 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

In realtà c'è un modo molto elegante per terminare la ricorsione:

Inizialmente ho provato template <typename Last> e template <typename First, typename ...Rest> ma quello era considerato ambiguo (Il resto può essere zero elementi). Questa domanda poi mi ha mostrato la soluzione definitiva: Compilation Error on Recursive Variadic Template Function


nota, al fine di evitare un po 'di duplicazione del codice, si potrebbe anche fare:

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

Questa dovrebbe essere la risposta accettata. –

+0

Si noti che questo non funziona se il pacchetto di parametri è vuoto, ma in caso contrario una soluzione molto bella. – zennehoy

+1

basta aggiungere un terzo sovraccarico per il caso vuoto (se lo si desidera) – Mordachai

4

In alternativa, è possibile decomprimere il parametro pacchetto in -place come nell'esempio seguente:

#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; 
} 

La ricorsione non è richiesta per farlo.

+0

errore di compilazione dovuto alla dichiarazione di funzione 'constexpr' (VS2015 Update 3). Tutto verrà compilato, se verrà rimosso –

+0

@skypkack, mi compilo con '/ std: C++ latest', cioè supporta' C++ 17'. Gli errori sono: 'Errore \t C3250 \t 'str': dichiarazione non è consentito in 'constexpr' la funzione del corpo \t' e 'Errore \t C3250 \t 'arr': dichiarazione non è consentita in funzione 'constexpr' corpo \t' –

+0

@IvanKush Codice aggiornato Grazie. – skypjack

Problemi correlati