Einen Vector expandieren



  • Hallo,

    ich bin auf etwas gestoßen, was ich mir nicht erklären kann. Ich möchte die Elemente eines Vektoren in der Parameterliste einer Callback-Funktion expandieren. Das muss doch irgendwie möglich sein.

    #include <vector>
    #include <functional>
    
    namespace detail{
        template<typename Func, typename... Args>
        void do_(Func f, const Args&... arguments){
            f(arguments...);
        }
    
        template<typename T, typename Func, std::size_t N, typename... Args>
        void expand(const std::vector<T>& v, Func callback, const Args&... arguments){
            if(sizeof...(arguments) < v.size())
                return expand<T, Func, N + 1>(v, callback, arguments..., v[N]);
    
        else
            do_(callback, arguments...);
        }
    }
    
    template<typename T, typename Func>
    void expand(const std::vector<T>& v, Func callback){
        detail::expand<T, Func, 0>(v, callback);
    }
    

    Hier bekomme ich eine Compile-Time-Endlosrekursion, wegen Zeile 13. Aber da in der Funktion detail::expand der Parameter-Pack immer größer wird, verstehe ich nicht, warum if(sizeof...(args)<v.size()) nicht irgendwann einmal abbricht.

    Kann mir das mal wer erklären?


  • Mod

    atze123 schrieb:

    [...] verstehe ich nicht, warum if(sizeof...(args)<v.size()) nicht irgendwann einmal abbricht.

    Kann mir das mal wer erklären?

    template <int I>
    struct fac {
        static const int value = I == 0 ? 1 : I * fac<I-1>::value;
    };
    

    funktoniert auch nicht. Aus dem gleichen Grund. Mal ganz abgesehen davon, dass v.size() kein konstanter Ausdruck ist.



  • Ok, aber mit std::array<> und std::tuple_size<>::size erzielt es den gleichen Effekt. Obwohl konstante Compile Time Expression. Oder was genau versteh ich da falsch?

    camper, dein Code sieht für mich irgendwie auch wie ne reine konstante Compile Time Expression aus. Liegt das an dem conditional if? Kann das nicht Compile Time sein, oder?

    Ich habs nochmal so probiert, aber partial template specialization ist bei Funktionen leider nicht erlaubt und das finde ich auch ausgesprochen doof. Mit Klassen ginge das ja, aber dann weiß ich nicht wie ich die Callback aufrufen soll. Ich glaub dann muss die auch ne constexpr Funktion sein oder so, aber das will ich nicht.

    template<typename Function, typename Array, std::size_t N, typename... Arguments>
    void f(Function&& callback, const Array& container, Arguments&&... arguments){
        f<Function, Array, N - 1>(callback, container, container[N], arguments...);
    }
    
    template<typename Function, typename Array, typename... Arguments>
    void f<Function, Array, 0, Arguments...>(Function&& function, const Array& array, Arguments&&... arguments){
        function(std::forward<Arguments>(arguments)...);
    }
    

    Wie ist mein Vorhaben möglich?


  • Mod

    atze123 schrieb:

    Ok, aber mit std::array<> und std::tuple_size<>::size erzielt es den gleichen Effekt. Obwohl konstante Compile Time Expression. Oder was genau versteh ich da falsch?

    Ja. Ich habe auf zwei Probleme hingewiesen, konstante Ausdrücke sind ist nur eines davon.
    Entscheidend ist zunächst einmal, dass Instantiierung von Templates und Auswertung von Ausdrücken nichts miteinander zu tun hat.
    Es spielt schlicht keine Rolle, welchen Wert eine Bedingung bei if, oder eines bedingten Ausdruckes hat - selbst wenn dieser bereits während des Compilierens bekannt ist - hinsichtlich der Instantiierung der Zweige der Bedingung. In

    if (true) foo(); else bar();
    

    wiseen wir, dass der else-Zweig nie betreten wird, syntaktisch korrekt muss er trotzdem sein, und Templates werden dafür ggf. instantiiert. Beim conditional-if kommt hinzu, dass wir den Typ des gesamten Ausdrucks nur kennen, wenn der Typ beider Zweige bekannt ist; das erfordert nat. ebenso eine Instantiierung. Gerade deshalb gehören Templatespezialisierungen zum Grundwerkzeug der Templateprogrammierung, weil Rekursionen anders nicht abzuschliessen sind.

    Im Zusammenhang mit Paramterpacks ist es häufig so, dass diese indiziert werden können; das Problem eine Reihe von n Werten oder Typen zu erzeugen, kann dann einfach auf das Problem, eine Sequenz 0,1,...,n-1 zu erzeugen, zurückgeführt werden. Weil das so ist, gibt es ab C++14 auch in der Standardbibliothek Mittel hierfür.

    template <typename Function, typename Container, std::size_t... I>
    constexpr auto apply(Function&& f, Container&& c, std::index_sequence<I...>) {
        return std::forward<Function>(f)(std::forward<Container>(c)[I]...);
    }
    
    template <typename Function, typename T, std::size_t N>
    constexpr auto apply(Function&& f, T(&a)[N]) {
        return apply(std::forward<Function>(f), a, std::make_index_sequence<N>{});
    }
    

Anmelden zum Antworten