2011-08-19 11 views

Odpowiedz

30

Musisz użyć częściowej specjalizacji, aby zakończyć rekursję, ale ponieważ nie możesz częściowo specjalizować bezpłatnych funkcji w C++, musisz utworzyć klasę implementacji z funkcją statycznego elementu.

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

To pierwsza deklaracja Impl tylko a workaround for a shortcoming in g++ 4.6 (i poniżej). Nie będzie to konieczne po prawidłowym wdrożeniu szablonów variadycznych.

Check it out in action at ideone.com

+0

Link, który cytujesz, jest martwy. Czy potrafisz opracować poprawny sposób, aby to zrobić bez błędnego g ++? – cdhowie

+0

To obejście jest również konieczne w przypadku Clang na komputerze Mac. – Gerard

+0

Chociaż ta odpowiedź jest prawidłowa, jest nieaktualna. Możliwe jest prostsze podejście, zobacz inne odpowiedzi. – Johannes

13

Jako alternatywę dla nieistniejącego częściowej specjalizacji dla funkcji, można użyć przeciążenie w klasie 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

Jest rzeczywiście bardzo elegancki sposób na zakończenie rekurencji:

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...>(); 
} 

Początkowo wypróbowałem template <typename Last> i template <typename First, typename ...Rest>, ale uznano to za niejednoznaczne (można odpocząć zero elementów). To pytanie następnie pokazał mi ostateczne rozwiązanie: Compilation Error on Recursive Variadic Template Function


Uwaga, aby uniknąć kawałek kodu powielania, można również zrobić:

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

To powinna być zaakceptowana odpowiedź. –

+0

Należy zauważyć, że to nie działa, jeśli pakiet parametrów jest pusty, ale poza tym bardzo ładne rozwiązanie. – zennehoy

+1

wystarczy dodać trzecie przeciążenie dla pustej obudowy (jeśli chcesz) – Mordachai

4

Jako alternatywę można rozpakować pakiet parametru w -place jak w poniższym przykładzie:

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

Rekursja nie jest wymagana do tego.

+0

błąd kompilacji z powodu deklaracji funkcji constexpr (VS2015 Update 3). Wszystko się skompiluje, jeśli zostanie usunięte –

+0

@skypkack, kompiluję z '/ std: C++ latest', to znaczy obsługuje' C++ 17'. Błędy są: 'Error \t C3250 \t 'str': oświadczenie nie jest dozwolone w 'constexpr' ciele funkcji \t' i 'Error \t C3250 \t 'arr': oświadczenie nie jest dozwolone w ciele funkcji 'constexpr' \t' –

+0

@IvanKush Zaktualizowano kod. Dziękuję Ci. – skypjack