2013-03-08 43 views
5

w ampułko-11 C++ miałem coś takiego:o zmiennej liczbie argumentów szablonu klasy bazowej przekazywanie połączeń

template<class T,class U,class V> 
struct Foo : T,U,V { 

    bool init() { 

    if(!T::init() || !U::init() || !V::init()) 
     return false; 

    // do local init and return true/false 
    } 
}; 

chciałbym przekonwertować to do C++ 11 składni o zmiennej liczbie argumentów, aby uzyskać korzyści długości elastycznego argumentu lista. Rozumiem koncepcję rozpakowywania listy szablonów arg przy użyciu rekursji, ale nie widzę prawidłowej składni. Oto, co starałem:

template<typename... Features> 
struct Foo : Features... { 

    template<typename F,typename... G> 
    bool recinit(F& arg,G&& ...args) { 

    if(!F::init()) 
     return false; 

    return recinit<F,G...>(args...); 
    } 

    bool init() { 
    // how to call recinit() from here? 
    } 
}; 

Wolałbym kolejność połączeń do klasy bazowej init() funkcje do lewej do prawej, ale to nie jest krytyczna.

+2

Nie jesteś pewien, co próbujesz osiągnąć: twoja funkcja 'recinit' jest nieskończenie rekursywna, czy też się mylę? jak spodziewasz się wywołać to z 'init()', z jakich argumentów? –

+3

Zatrzymaj używanie metody 'init' i użyj konstruktora. –

Odpowiedz

4

To powinno działać:

template<typename F, typename... T> 
    struct recinit; 

template<typename F> 
    struct recinit<F> { 
     static bool tinit(F *) { 
      return true; 
     } 
    }; 
template<typename F, typename T, typename... G> 
    struct recinit<F, T, G...> { 
     static bool tinit(F *ptr) { 
      if (!ptr->T::init()) 
       return false; 
      return recinit<F, G...>::tinit(ptr); 
     } 
    }; 

template<typename... Features> 
struct Foo : Features... { 

    bool init() { 
     bool res = recinit<Foo, Features...>::tinit(this); 
     //use res wisely 
    } 
}; 

Twoim problemem jest to, że nie można napisać częściowych specjalizacji funkcji, tylko z lekcji/elemencie. A pomocnicza struktura musi znajdować się poza Foo, inaczej otrzyma argumenty szablonu z otaczającej struktury, a to byłoby złe.

Nie mów, ale zakładam, że init jest niestatyczną funkcją składową. Jeśli tak jest, argumenty args nie mają większego sensu: wszystkie powinny być this! Po prostu pomiń ten jeden raz i unikaj paczki w argumentach. Próbowałem przekazać this jako void*, ale może to być kłopotliwe, dlatego właśnie dodałem dodatkowy argument szablonu do recinit, który będzie Foo.

A także, za każdym razem, gdy wykonujesz jeden krok rekurencyjny, pamiętaj o usunięciu jednego parametru.

+0

Chociaż jestem pewien, że wszystkie głosowane odpowiedzi są dobre, to ten najbardziej odpowiada moim wymaganiom. Podłączyłem go do mojej hierarchii i działa idealnie, a Rodrigo miał rację, zakładając, że init() jest niestatyczną funkcją składową. Nauczyłem się czegoś nowego dzisiaj, także w piątek. Dobry towar. –

+0

@AndyBrown: Ponownie czytając moją odpowiedź, 'static_cast' nie jest tak naprawdę potrzebna, ponieważ' T' musi być podstawową klasą 'F'. Usunięty, kod jest zawsze ładniejszy bez rzutowania. – rodrigo

3

Może spróbuj coś takiego:

template<typename... Features> 
struct Foo : Features... 
{ 
    bool init() 
    { 
     // Courtesy of Xeo :-) 
     auto il = {(static_cast<bool (Foo::*)()>(&Features::init))...}; 
     return std::all_of(il.begin(), il.end(), 
      [this] (bool (Foo::*f)()) { return (this->*f)(); } 
      ); 
    } 
}; 

Oto alternatywna wersja bardziej gadatliwy, który używa zmiennej liczbie argumentów szablony:

template<typename... Features> 
struct Foo : Features... 
{ 
    bool init() 
    { 
     return combine_and((&Features::init)...); 
    } 

private: 

    bool combine_and() 
    { 
     return true; 
    } 

    template<typename F> 
    bool combine_and(F f) 
    { 
     return (this->*f)(); 
    } 

    template<typename F1, typename... Fs> 
    bool combine_and(F1 f1, Fs... fs) 
    { 
     return ((this->*f1)() && combine_and(fs...)); 
    } 
}; 

Którykolwiek rozwiązanie wybrać, to w jaki sposób można użyć to:

#include <iostream> 

using namespace std; 

struct A { bool init() { cout << "Hello " << endl; return true; } }; 
struct B { bool init() { cout << "Template " << endl; return true; } }; 
struct C { bool init() { cout << "World!" << endl; return true; } }; 

int main() 
{ 
    Foo<A, B, C> f; 
    bool res = f.init(); // Prints "Hello Template World!" 
    cout << res; // Prints 1 
} 
+0

Nie zbiera jednak wartości zwracanych z funkcji 'init's. Powinien być bardziej podobny do 'fold ();'. – Xeo

+0

@Xeo: Edytowałem odpowiedź. Ta wydaje mi się OK. –

+2

'auto il = {Funkcje :: init() ...}; return std :: all_of (il.begin(), il.end(), [] (bool b) {return b;}); ':) – Xeo

2

Twój kod ma dwa problemy. Po pierwsze, raczej przyziemne:

return recinit<F,G...>(args...); 

Już obsługiwane F pozostawić ją z listy argumentów.

return recinit<G...>(args...); 

(Dodatkowo należy zapewne doskonale przekazuje argumenty.)

drugie, kod nie będzie kompilować, ponieważ rekurencji ma kotwicę przy starcie ale nie w czasie kompilacji. Oznacza to, że kompilator spróbuje rozpakować pakiet argumentów w nieskończoność. Aby temu zapobiec, musisz wyspecyfikować funkcję dla pustej listy argumentów szablonu.

Powiązane problemy