Vorlagentupel – Aufruf einer Funktion für jedes Element

Lesezeit: 10 Minuten

Vorlagentupel Aufruf einer Funktion fur jedes Element
7 Kühe

Meine Frage steht im Code:

template<typename... Ts>
struct TupleOfVectors {
  std::tuple<std::vector<Ts>...> tuple;

  void do_something_to_each_vec() {
    //Question: I want to do this:
    //  "for each (N)": do_something_to_vec<N>()
    //How?
  }

  template<size_t N>
  void do_something_to_vec() {
    auto &vec = std::get<N>(tuple);
    //do something to vec
  }
};

  • Mögliches Duplikat von iterate over tuple

    – Justin

    18. August 17 um 2:08 Uhr

Vorlagentupel Aufruf einer Funktion fur jedes Element
Andy Prowl

Sie können das ganz einfach mit einigen Index-Maschinen tun. Gegeben eine Metafunktion gen_seq zum Generieren von Integer-Sequenzen zur Kompilierzeit (gekapselt durch die seq Klassenvorlage):

namespace detail
{
    template<int... Is>
    struct seq { };

    template<int N, int... Is>
    struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };

    template<int... Is>
    struct gen_seq<0, Is...> : seq<Is...> { };
}

Und die folgenden Funktionsvorlagen:

#include <tuple>

namespace detail
{
    template<typename T, typename F, int... Is>
    void for_each(T&& t, F f, seq<Is...>)
    {
        auto l = { (f(std::get<Is>
    }
}

template<typename... Ts, typename F>
void for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
    detail::for_each(t, f, detail::gen_seq<sizeof...(Ts)>());
}

Du kannst den … benutzen for_each_in_tuple Funktion oben auf diese Weise:

#include <string>
#include <iostream>

struct my_functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    std::tuple<int, double, std::string> t(42, 3.14, "Hello World!");
    for_each_in_tuple(t, my_functor());
}

Hier ist ein Live-Beispiel.

In Ihrer konkreten Situation könnten Sie es so verwenden:

template<typename... Ts>
struct TupleOfVectors
{
    std::tuple<std::vector<Ts>...> t;

    void do_something_to_each_vec()
    {
        for_each_in_tuple(t, tuple_vector_functor());
    }

    struct tuple_vector_functor
    {
        template<typename T>
        void operator () (T const &v)
        {
            // Do something on the argument vector...
        }
    };
};

Und noch einmal, hier ist ein Live-Beispiel.

Aktualisieren

Wenn Sie verwenden C++14 oder später können Sie die ersetzen seq und gen_seq Klassen oben mit std::integer_sequence so:

namespace detail
{
    template<typename T, typename F, int... Is>
    void
    for_each(T&& t, F f, std::integer_sequence<int, Is...>)
    {
        auto l = { (f(std::get<Is>
    }
} // namespace detail

template<typename... Ts, typename F>
void
for_each_in_tuple(std::tuple<Ts...> const& t, F f)
{
    detail::for_each(t, f, std::make_integer_sequence<int, sizeof...(Ts)>());
}

Wenn Sie verwenden C++17 oder später können Sie dies tun (aus diesem Kommentar unten):

std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);

  • Metaprogrammierung frisst mein Gehirn. nicht sicher, wo die Dinge beginnen, was was ruft und was/wo das Endergebnis ist. überhaupt nicht wie normale Programmierung. Wird einige Zeit dauern, um das zu entschlüsseln 🙂

    – 7 Kühe

    5. Mai 13 um 18:27 Uhr

  • @7cows: Natürlich. Am Anfang ist es überhaupt nicht einfach, und vielleicht ist dies nicht gerade das einfachste Beispiel für den Anfang, aber ich bin sicher, mit etwas Übung werden Sie es bald verstehen 😉

    – Andy Prowl

    5. Mai 13 um 18:28 Uhr


  • void for_each(T&& t, F f, seq<Is...>): Warum hat das letzte Argument keinen Bezeichner?

    – Vorlagenjunge

    5. Mai ’13 um 18:30 Uhr


  • Der auto l soll es ermöglichen, das, was auf der rechten Seite steht, als Initialisierungsliste zu behandeln, wodurch sichergestellt wird, dass die erweiterten Ausdrücke in der richtigen Reihenfolge ausgewertet werden. Der (f(x), 0) verwendet den Komma-Operator, damit f(x) ausgewertet, aber der resultierende Wert wird verworfen, und der Wert des Ausdrucks (f(x), 0) ist 0. Dies dient dazu, allen Elementen der Initialisierungsliste den gleichen Typ zu geben (int, hier), um auf den Typ der Initialisierungsliste schließen zu können (initializer_list<int>)

    – Andy Prowl

    5. Mai 13 um 20:13 Uhr

  • So viele Jahre später konnte der erste Teil durch ersetzt werden std::integer_sequence?

    – JH Bonarius

    3. Juli 18 um 20:24 Uhr

Vorlagentupel Aufruf einer Funktion fur jedes Element
Mohammed Alaggan

In C++17 können Sie dies tun:

std::apply([](auto ...x){std::make_tuple(some_function(x)...);} , the_tuple);

gegeben das some_function verfügt über geeignete Überladungen für alle Typen im Tupel.

Dies funktioniert bereits in Clang++ 3.9 mit std::experimental::apply.

  • Führt dies nicht zur Iteration – dh Aufrufen von do_something() – in unbestimmter Reihenfolge auftreten, da das Parameterpaket innerhalb eines Funktionsaufrufs expandiert wird (), wobei Argumente eine unbestimmte Reihenfolge haben? Das könnte sehr bedeutsam sein; Ich würde mir vorstellen, dass die meisten Leute erwarten würden, dass die Reihenfolge garantiert in der gleichen Reihenfolge wie die Mitglieder erfolgt, dh wie die Indizes std::get<>(). AFAIK, um in solchen Fällen eine garantierte Bestellung zu erhalten, muss die Erweiterung innerhalb von erfolgen {braces}. Liege ich falsch? Diese Antwort betont eine solche Reihenfolge: stackoverflow.com/a/16387374/2757035

    – Unterstrich_d

    9. Oktober 16 um 14:42 Uhr

  • @underscore_d C++17 garantiert die Ausführungsreihenfolge von Funktionsargumenten. Da diese Antwort für C++17 gilt, ist dies gültig.

    – Guillaume Racicot

    27. April 17 um 17:08 Uhr

  • @GuillaumeRacicot Mir sind einige Änderungen an der Bewertungsreihenfolge/Garantien in bestimmten Kontexten bekannt, aber nicht in Argumenten für Funktionen – abgesehen von einigen Hin- und Herbewegungen, bei denen die Reihenfolge von links nach rechts war berücksichtigt aber abgelehnt. Schauen Sie sich den aktuellen Entwurf der Norm an: github.com/cplusplus/draft/blob/master/source/… The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.

    – Unterstrich_d

    28. April 17 um 21:59 Uhr


  • std::apply([](auto&& ...x){ (static_cast<void>(some_function(std::forward<decltype(x)>(x))), ...);} , the_tuple); um die Reihenfolge der Auswertung zu gewährleisten, und ermöglicht some_function zurückgeben void (und sogar böse Klassen mit überladenen operator ,).

    – Jarod42

    9. Februar 18 um 13:12 Uhr


  • @ Jarod42 da wir hier über c ++ 17 sprechen, ein Faltausdruck wie std::apply([](auto& ...x){(..., some_function(x));}, the_tuple); ist besser stackoverflow.com/a/45498003/8414561

    – Entwickler Null

    21. April 18 um 10:48 Uhr

1644075067 73 Vorlagentupel Aufruf einer Funktion fur jedes Element
Entwickler Null

Neben der Antwort von @M. Alaggan, wenn Sie eine Funktion für Tupelelemente aufrufen müssen in der Reihenfolge ihres Erscheinens im Tupel können Sie in C++17 auch einen Fold-Ausdruck wie diesen verwenden:

std::apply([](auto& ...x){(..., some_function(x));}, the_tuple);

(Live-Beispiel).

Weil sonst Die Reihenfolge der Auswertung von Funktionsargumenten ist nicht angegeben.

1644075067 515 Vorlagentupel Aufruf einer Funktion fur jedes Element
Vaughn Cato

Hier ist ein Ansatz, der in Ihrem Fall gut funktionieren könnte:

template<typename... Ts>
struct TupleOfVectors {
    std::tuple<std::vector<Ts>...> tuple;

    void do_something_to_each_vec()
    {
        // First template parameter is just a dummy.
        do_something_to_each_vec_helper<0,Ts...>();
    }

    template<size_t N>
    void do_something_to_vec()
    {
        auto &vec = std::get<N>(tuple);
        //do something to vec
    }

private:
    // Anchor for the recursion
    template <int>
    void do_something_to_each_vec_helper() { }

    // Execute the function for each template argument.
    template <int,typename Arg,typename...Args>
    void do_something_to_each_vec_helper()
    {
        do_something_to_each_vec_helper<0,Args...>();
        do_something_to_vec<sizeof...(Args)>();
    }
};

Das einzige, was hier etwas chaotisch ist, ist der zusätzliche Dummy int Vorlagenparameter zu do_something_to_each_vec_helper. Es ist notwendig, dass do_something_to_each_vec_helper immer noch eine Vorlage ist, wenn keine Argumente verbleiben. Wenn Sie einen anderen Vorlagenparameter verwenden möchten, können Sie ihn stattdessen dort verwenden.

Wenn Sie nicht besonders an einer Lösung in Form einer generischen “für jede” Funktionsvorlage interessiert sind, können Sie eine wie diese verwenden:

#ifndef TUPLE_OF_VECTORS_H
#define TUPLE_OF_VECTORS_H

#include <vector>
#include <tuple>
#include <iostream>

template<typename... Ts>
struct TupleOfVectors 
{
    std::tuple<std::vector<Ts>...> tuple;

    template<typename ...Args>
    TupleOfVectors(Args... args)
    : tuple(args...){}

    void do_something_to_each_vec() {
        do_something_to_vec(tuple);
    }

    template<size_t I = 0, class ...P>
    typename std::enable_if<I == sizeof...(P)>::type
    do_something_to_vec(std::tuple<P...> &) {}

    template<size_t I = 0, class ...P>
    typename std::enable_if<I < sizeof...(P)>::type
    do_something_to_vec(std::tuple<P...> & parts) {
        auto & part = std::get<I>(tuple);
        // Doing something...
        std::cout << "vector[" << I << "][0] = " << part[0] << std::endl;
        do_something_to_vec<I + 1>(parts);
    }
};

#endif // EOF

Ein Testprogramm, gebaut mit GCC 4.7.2 und clang 3.2:

#include "tuple_of_vectors.h"

using namespace std;

int main()
{
    TupleOfVectors<int,int,int,int> vecs(vector<int>(1,1),
        vector<int>(2,2),
        vector<int>(3,3),
        vector<int>(4,4));

    vecs.do_something_to_each_vec();
    return 0;
}

Derselbe Rekursionsstil kann in einer generischen “for_each”-Funktionsvorlage ohne Hilfsindexapparat verwendet werden:

#ifndef FOR_EACH_IN_TUPLE_H
#define FOR_EACH_IN_TUPLE_H

#include <type_traits>
#include <tuple>
#include <cstddef>

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> &, Func) {}

template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> & tpl, Func func) 
{
    func(std::get<I>(tpl));
    for_each_in_tuple<I + 1>(tpl,func);
}

#endif //EOF

Und dazu ein Testprogramm:

#include "for_each_in_tuple.h"
#include <iostream>

struct functor
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

int main()
{
    auto tpl = std::make_tuple(1,2.0,"Three");
    for_each_in_tuple(tpl,functor());
    return 0;
}

  • Diese Antwort war für mich viel hilfreicher und erzeugt keine Warnungen wie einige der anderen. +1

    – Brad Allred

    29. November 13 um 16:22 Uhr

Ich habe mit Tupeln und Metaprogrammierung getestet und den aktuellen Thread gefunden. Ich denke, meine Arbeit kann jemand anderen inspirieren, obwohl ich die Lösung von @Andy mag.

Wie auch immer, macht einfach Spaß!

#include <tuple>
#include <type_traits>
#include <iostream>
#include <sstream>
#include <functional>

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I != std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
    func(std::get<I>(tuple));
    for_each<I + 1>(tuple, func);
}

template<std::size_t I = 0, typename Tuple, typename Func>
typename std::enable_if< I == std::tuple_size<Tuple>::value, void >::type
for_each(const Tuple& tuple, Func&& func)
{
    // do nothing
}


struct print
{
    template<typename T>
    void operator () (T&& t)
    {
        std::cout << t << std::endl;
    }
};

template<typename... Params>
void test(Params&& ... params)
{
    int sz = sizeof...(params);
    std::tuple<Params...> values(std::forward<Params>(params)...);
    for_each(values, print() );
}


class MyClass
{
public:
    MyClass(const std::string& text) 
        : m_text(text)
    {
    }

    friend std::ostream& operator <<(std::ostream& stream, const MyClass& myClass)
    {
        stream << myClass.m_text;
        return stream;
    }

private:
    std::string m_text;
};


int main()
{
    test(1, "hello", 3.f, 4, MyClass("I don't care") );
}

  • Diese Antwort war für mich viel hilfreicher und erzeugt keine Warnungen wie einige der anderen. +1

    – Brad Allred

    29. November 13 um 16:22 Uhr

1644075068 953 Vorlagentupel Aufruf einer Funktion fur jedes Element
NoSenseEtAl

Boost mp11 hat diese Funktionalität:

#include <iostream>
#include <string>
#include <boost/mp11.hpp>

using namespace std;
using boost::mp11::tuple_for_each;

std::tuple t{string("abc"), 47 };

int main(){
    tuple_for_each(t,[](const auto& x){
        cout << x + x << endl;
    });
}

.

784830cookie-checkVorlagentupel – Aufruf einer Funktion für jedes Element

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy