2013-05-10 29 views
10

Załóżmy, że jest to funkcja, która przyjmuje kilka napisów:Konwersja zmiennej liczbie argumentów szablonu pakiet do std :: initializer_list

void fun (const std::initializer_list<std::string>& strings) { 
    for(auto s : strings) 
    // do something 
} 

Teraz mam o zmiennej liczbie argumentów template funkcję powiedzieć foo() jak:

template<typename ...Args> 
void foo() { 
    fun(???); 
} 

ten Metoda nazywa się zewnętrznie jako:

foo<A, B, C, D>(); // where A, B, C, D are classes 

A te classe s, które są przekazywane jako argumenty Oczekuje się, że zawierają wspólny static const użytkownika:

static const std::string value = "..."; 

Oto moje pytania (jak je zdobyć):

  1. Kiedy wewnątrz foo(), sprawdź czy wszystkie Args zawierać value użyciu static_assert
  2. Przekaż wszystkie takie wartości do fun(), aby utworzyć initializer_list; na przykład fun({A::value, B::value, ...});

Szukano kilka wątków związanych zmiennej liczbie argumentów szablonów i jego rozpakowaniu, ale nadal jestem nowicjuszem w tej dziedzinie. Wyjaśnienie trochę więcej szczegółów jest bardzo doceniane.

Odpowiedz

7

Co do drugiego pytania, po prostu zrób to w ten sposób:

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

Mechanizm jest dość intuicyjny: Ci stworzyć lista initalizera, która zawiera rozwinięty wzorzec Args::value, w ten sposób rozwiązując (w twoim przypadku) do { A::value, B::value, C::value, D::value }.

Oto kompletny program:

#include <string> 
#include <iostream> 

void fun (const std::initializer_list<std::string>& strings) { 
    for(auto s : strings) 
    { 
     std::cout << s << " "; 
    } 
} 

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

struct A { static std::string value; }; 
struct B { static std::string value; }; 
struct C { static std::string value; }; 
struct D { static std::string value; }; 

std::string A::value = "Hello"; 
std::string B::value = "World"; 
std::string C::value = "of"; 
std::string D::value = "Variadic Templates"; 

int main() 
{ 
    foo<A, B, C, D>(); // where A, B, C, D are classes 
} 

A oto live example.

chodzi o statycznym twierdzenia, można napisać cechę typ, który określa, czy dany typ ma zmienną składową value:

template<typename T, typename V = bool> 
struct has_value : std::false_type { }; 

template<typename T> 
struct has_value<T, 
    typename std::enable_if< 
     !std::is_same<decltype(std::declval<T>().value), void>::value, 
     bool 
     >::type 
    > : std::true_type 
{ 
    typedef decltype(std::declval<T>().value) type; 
}; 

Potem, można go używać w ten sposób:

template<typename T> 
struct check_has_value 
{ 
    static_assert(has_value<T>::value, "!"); 
}; 

template<typename ...Args> 
void foo() { 
    auto l = { (check_has_value<Args>(), 0)... }; 
    fun({Args::value...}); 
} 

Oto live example udanego sprawdzenia (wszystkie klasy mają element danych value). Oto live example od nieudanej czeku (klasa D jest członkiem dane nazywa values)

3

Druga część jest łatwiejsza:

template<typename ...Args> 
void foo() { 
    fun({Args::value...}); 
} 

Pierwsza część jest trudne, ponieważ static_assert jest deklaracja, a nie wyrazem, więc trzeba by rozszerzyć o zmiennej liczbie argumentów paczkę w ciągu pierwszego parametru. Może być łatwiej po prostu zadzwonić pod numer fun. Oto szkic jak to zrobić za pomocą dodatkowego allconstexpr funkcję:

constexpr bool all() { return true; } 
template<typename... Args> constexpr bool all(bool first, Args&&... rest) { 
    return first && all(rest...); 
} 

template<typename ...Args> 
void foo() { 
    static_assert(all(std::is_convertible<decltype(Args::value), 
     std::string>::value...), "All Args must have a value"); 
    fun({Args::value...}); 
} 
+0

Oh Tak łatwo ... masz rację co do 'fun()' wykonania części; chciałem tylko uratować się od złych błędów kompilatora. :) – iammilind

+0

Jak to jest sprawdzanie, czy wszystkie 'Args ...' ma element 'value'? – 0x499602D2

+0

@ 0x499602D2 zanotuj rozszerzenie paczki wewnątrz wywołania do 'all()'. – ecatmur

1

Oto odpowiedź na oba punkty:

#include <initializer_list> 
#include <iostream> 
#include <string> 
#include <type_traits> 

using namespace std; 

void fun (const std::initializer_list<std::string>& strings) { 
    for(auto s : strings) 
    cout << s << endl; 
} 

// This uses SFINAE to find if there's a string T::value in T 
template <typename T> 
struct HasValue 
{ 
    typedef char OK; //sizeof() guaranteed 1 
    struct BAD { char x[2]; }; //sizeof() guaranteed >1 

    template <const string *> 
    struct Helper; 

    template <typename X> 
    static OK has(X*, Helper<&X::value>* = nullptr); //SF if &X::value is not a const string* 

    static BAD has(...); //will be picked in SF case 

    static const bool value = (sizeof(has((T*)nullptr)) == sizeof(OK)); 
}; 


// This template (and its specialisation) ensure all args have ::value 
template <typename H, typename... T> 
struct HaveValue : public integral_constant<bool, HasValue<H>::value && HaveValue<T...>::value> 
{}; 

template <typename H> 
struct HaveValue<H> : public HasValue<H> 
{}; 



template <typename... Args> 
void foo() { 
    static_assert(HaveValue<Args...>::value, "All arguments must have const string ::value"); 
    fun({Args::value...}); //answer to point 2: create the initialiser list 
} 

// Example data follow 
struct A 
{ 
    static const string value; 
}; 
const string A::value = "AA"; 

struct B 
{ 
    static const string value; 
}; 
const string B::value = "BB"; 

struct C{}; 

int main() 
{ 
    foo<A, B>(); 
    //foo<A, B, C>(); //uncomment to have the static assertion fire 
} 

See it live.

Powiązane problemy