2010-09-06 12 views
5

Jestem trochę zdezorientowany, w jaki sposób można odczytać każdy argument z krotki za pomocą szablonów variadic.odczytać argumenty z szablonu variadic

Rozważmy tę funkcję:

template<class...A> int func(A...args){ 
int size = sizeof...(A); 
.... } 

nazywam go z pliku głównego jak:

func(1,10,100,1000); 

teraz, ja nie wiem, jak mam się rozciągać ciało func aby móc przeczytać każdy argument osobno, aby móc na przykład przechowywać argumenty w tablicy.

+3

Czego brakowało ci w poprzednim pytaniu? (http://stackoverflow.com/questions/3634379/variadic-templates) A ​​dlaczego nie akceptujesz odpowiedzi na swoje pytania? – ereOn

+0

http://stackoverflow.com/questions/3634379/variadic-templates/3645307#3645307 – kennytm

+1

Odpowiedź powinna znajdować się w połączonym wątku (przez Motti, nie ma awersji?) – visitor

Odpowiedz

11

Musisz podać przesłonięcia dla funkcji do zużywania pierwszych N (zazwyczaj jednego) argumentów.

void foo() { 
    // end condition argument pack is empty 
} 

template <class First, class... Rest> 
void foo(First first, Rest... rest) { 
    // Do something with first 
    cout << first << endl; 

    foo(rest...); // Unpack the arguments for further treatment 
} 

Po rozpakowaniu parametru variadic znajduje następne przeciążenie.

Przykład:

foo(42, true, 'a', "hello"); 
// Calls foo with First = int, and Rest = { bool, char, char* } 
// foo(42, Rest = {true, 'a', "hello"}); // not the real syntax 

Potem następny poziom w dół rozszerzymy poprzedni Rest i otrzymujemy:

foo(true, Rest = { 'a', "hello"}); // First = bool 

I tak dalej, aż Rest zawiera żadnych członków w tym przypadku rozpakowywania nazywa to foo() (przeciążenie bez argumentów).


Przechowywanie pakiet jeśli różne typy

Jeśli chcesz zapisać cały argumentem pakować można użyć std::tuple

template <class... Pack> 
void store_pack(Pack... p) { 
    std::tuple<Pack...> store(p...); 
    // do something with store 
} 

to jednak wydaje się mniej użyteczne.

Przechowywanie pakiet jeśli jest jednorodny

Jeśli wszystkie wartości w zestawie są tego samego typu można przechowywać je wszystkie tak:

vector<int> reverse(int i) { 
    vector<int> ret; 
    ret.push_back(i); 
    return ret; 
} 

template <class... R> 
vector<int> reverse(int i, R... r) { 
    vector<int> ret = reverse(r...); 
    ret.push_back(i); 
    return ret; 
} 

int main() { 
    auto v = reverse(1, 2, 3, 4); 
    for_each(v.cbegin(), v.cend(), 
     [](int i) { 
      std::cout << i << std::endl; 
     } 
    ); 
} 

Jednak wydaje nawet mniej użyteczne.

+0

@Motti: Zrozumiałem, co masz na myśli. Ale wciąż mam dwa pytania: 1- jeśli chcę określić typ danych, czy mogę wywołać funkcję foo w następujący sposób: foo (42, true, "a", "hello") 2 - gdzie mogłem odczytać wartości do przechowywania, gdzie chcę je przechowywać po rozpakowaniu i to było moje główne pytanie! – sami

+0

@sami, 1. Nie jestem pewien co standard mówi, ale g ++ akceptuje 'foo (42)' w takim przypadku otrzymuję '*' zamiast '42' co oznacza, że ​​zmusił argument do' char'. – Motti

+0

@sami, 2. Jeśli są one różnych typów, nie możesz ich przechowywać w regularnej tablicy (chyba że użyjesz 'boost :: any' lub czegoś podobnego, jak sugerował Kirill). – Motti

1

Jeśli trzeba przechowywać argumentów w tablicy można użyć tablicę boost::any następująco:

template<typename... A> int func(const A&... args) 
{ 
    boost::any arr[sizeof...(A)] = { args... }; 
    return 0; 
} 
3

Jeśli argumenty są tego samego typu, można przechowywać argumenty w tablicy tak (stosując typ pierwszy argument na tablicy):

template <class T, class ...Args> 
void foo(const T& first, const Args&... args) 
{ 
    T arr[sizeof...(args) + 1] = { first, args...}; 
} 

int main() 
{ 
    foo(1); 
    foo(1, 10, 100, 1000); 
} 

Jeśli typy są różne, przypuszczam, że można użyć boost::any ale nie widzę w jaki sposób masz zamiar dowiedzieć się na zewnątrz danego szablonu , która pozycja jest tego typu (jak się masz g, aby użyć zapisanych wartości).


Edit: Jeśli argumenty są tego samego typu i chcesz przechowywać je w pojemniku STL, można raczej użyć std::initializer_list<T>. Na przykład, przykład MOTTI za przechowywania wartości w odwrotnej kolejności:

#include <vector> 
#include <iostream> 
#include <iterator> 

template <class Iter> 
std::reverse_iterator<Iter> make_reverse_iterator(Iter it) 
{ 
    return std::reverse_iterator<Iter>(it); 
} 

template <class T> 
std::vector<T> reverse(std::initializer_list<T> const & init) 
{ 

    return std::vector<T>(make_reverse_iterator(init.end()), make_reverse_iterator(init.begin())); 
} 

int main() { 
    auto v = reverse({1, 2, 3, 4}); 
    for (auto it = v.begin(); it != v.end(); ++it) { 
     std::cout << *it << std::endl; 
    } 
} 
+0

czy biblioteka boost obsługuje również kontenery STL dla szablonów variadic? – sami

+0

o co chodzi, jeśli argumenty nie pochodzą z tego samego typu danych? – sami

+0

Standardowym "pojemnikiem" dla wartości różnych typów jest 'std :: tuple'. – UncleBens

1

do przyklejania do tablicy jeśli argumenty mają różne typy, można użyć również std::common_type<>

template<class ...A> void func(A ...args){ 
    typedef typename common_type<A...>::type common; 
    std::array<common, sizeof...(A)> a = {{ args... }}; 
} 

Tak na przykład func(std::string("Hello"), "folks") tworzy tablicę z std::string.

Powiązane problemy