2017-08-18 4 views
0

Wie kann ich ein Funktionsobjekt definieren, das übergeben werden kann? Letztendlich möchte ich eine Funktion F f, die alle Elemente des Tupels aufnehmen und zu einem Vektor zurückschieben kann.Funktionsobjekte mit variadischer Vorlage

template <class F, size_t... Is> 
constexpr auto index_apply_impl(F f, index_sequence<Is...>) { 
    return f(integral_constant<size_t, Is> {}...); 
} 

template <size_t N, class F> 
constexpr auto index_apply(F f) { 
    return index_apply_impl(f, make_index_sequence<N>{}); 
} 

template <class Tuple, class F> 
constexpr auto apply(Tuple t, F f) { 
    return index_apply<tuple_size<Tuple>{}>(
     [&](auto... Is) { return f(get<Is>(t)...); }); 
} 

Thanks :)

+0

Die Idee ist sehr schön, aber es wäre ein Vektor von dem, was sein? –

+0

Mögliches Duplikat von [iterate over tuple] (https://stackoverflow.com/questions/1198260/iterate-over-tuple) – Justin

+0

Kein Duplikat. Diese spezielle Frage hat keine Antwort, da die einzige Art von Vektor-ähnlicher Sache, die das Ergebnis halten könnte, ein Tupel wäre, das eine Kopie der Eingabe ist. –

Antwort

1

Sie die expander trick verwenden können. Dies benötigt jedoch C++ 14. Ich weiß nicht, wie Ihr Byte-Array-Format aussieht, also habe ich einfach eine generische Struktur mit einem String-Member genommen und alles im Konstruktor in String umgewandelt.

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

template < typename T, typename F, size_t ... Is > 
void for_each_impl(T&& t, F&& f, std::index_sequence<Is...>) 
{ 
    using expand_type = int[]; 
    (void) expand_type { 0, ((void) f(std::get<Is>(t)), 0) ... }; 
} 

template < typename... Args, typename F > 
void for_each(std::tuple<Args...> const& t, F&& f) 
{ 
    for_each_impl(t, f, std::make_index_sequence<sizeof...(Args)>{}); 
} 


struct Bytes { 
    std::string str; 
    Bytes(char const * c) : str(c) {}; 
    Bytes(int i) : str(std::to_string(i)) {}; 
    Bytes(char c) : str(1, c) {}; 
}; 


int main() 
{ 
    auto t = std::make_tuple(12, "abc", 10, 'c', 1); 
    std::vector<Bytes> v; 
    for_each(t, [&v](auto&& x){ v.push_back(x); }); 

    for (auto const& e : v) 
    std::cout << e.str << ' '; 
    std::cout << '\n'; 
} 

Live example


In C++ 17 ist es viel einfacher, dank variadische Lambda, fold expression und std::apply.

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

struct Bytes { 
    std::string str; 
    Bytes(char const * c) : str(c) {}; 
    Bytes(int i) : str(std::to_string(i)) {}; 
    Bytes(char c) : str(1, c) {}; 
}; 


int main() 
{ 
    auto t = std::make_tuple(12, "abc", 10, 'c', 1); 
    std::vector<Bytes> v; 
    std::apply([&v](auto&&... x){ (... , v.push_back(x)); }, t); 

    for (auto const& e : v) 
    std::cout << e.str << ' '; 
    std::cout << '\n'; 
} 

Live example


Wenn Sie nicht C++ 11 Ihr Leben verwenden können, wird schmerzhaft sein. Sie müssen Ihre eigene Implementierung von index_sequence rollen und Sie müssen eine Helper-Struktur mit Vorlagen-Call-Operator definieren, um das generische Lambda zu ersetzen.

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

// https://stackoverflow.com/a/24481400/1944004 
template <size_t ...I> 
struct index_sequence {}; 

template <size_t N, size_t ...I> 
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {}; 

template <size_t ...I> 
struct make_index_sequence<0, I...> : public index_sequence<I...> {}; 


// Call a function for each element in a tuple 

template < typename T, typename F, size_t ... Is > 
void for_each_impl(T&& t, F&& f, index_sequence<Is...>) 
{ 
    using expand_type = int[]; 
    (void) expand_type { 0, ((void) f(std::get<Is>(t)), 0) ... }; 
} 

template < typename... Args, typename F > 
void for_each(std::tuple<Args...> const& t, F&& f) 
{ 
    for_each_impl(t, f, make_index_sequence<sizeof...(Args)>{}); 
} 

// "Byte array" emulation 

struct Bytes { 
    std::string str; 
    Bytes(char const * c) : str(c) {}; 
    Bytes(int i) : str(std::to_string(i)) {}; 
    Bytes(char c) : str(1, c) {}; 
}; 

// Surrogate template lambda 

struct Visitor 
{ 
    std::vector<Bytes>& v; 
    Visitor(std::vector<Bytes>& vb) : v(vb) {}; 
    template < typename T > 
    void operator() (T&& x) { v.push_back(x); } 
}; 


int main() 
{ 
    auto t = std::make_tuple(12, "abc", 10, 'c', 1); 
    std::vector<Bytes> v; 
    for_each(t, Visitor(v)); 

    for (auto const& e : v) 
    std::cout << e.str << ' '; 
    std::cout << '\n'; 
} 

Live example

+0

Danke Henri! Das war großartig! Gibt es einen Weg, dies in C++ 11 zu erreichen? – A03

+0

@ a03 Ja, schreibe das Lambda als Objekt mit einem 'template'' operator() 'neu. – Yakk

+0

@ A03 Siehe meine aktualisierte Antwort. –

Verwandte Themen