Betrachten Sie den Fall einer Vorlagenfunktion mit variadischen Vorlagenargumenten:
template<typename Tret, typename... T> Tret func(const T&... t);
Jetzt habe ich ein Tupel t
von Werten. Wie rufe ich an func()
die Tupelwerte als Argumente verwenden? Ich habe über die gelesen bind()
Funktionsobjekt, mit call()
Funktion, und auch die apply()
Funktion in verschiedenen, inzwischen veralteten Dokumenten. Die Implementierung von GNU GCC 4.4 scheint eine zu haben call()
Funktion in der bind()
Klasse, aber es gibt sehr wenig Dokumentation zu diesem Thema.
Einige Leute schlagen handgeschriebene rekursive Hacks vor, aber der wahre Wert von variadischen Template-Argumenten besteht darin, sie in Fällen wie oben verwenden zu können.
Hat jemand eine Lösung dafür, oder einen Hinweis, wo man darüber lesen kann?

Mohammed Alaggan
In C++17 können Sie dies tun:
std::apply(the_function, the_tuple);
Dies funktioniert bereits in Clang++ 3.9 mit std::experimental::apply.
Als Antwort auf den Kommentar, dass dies nicht funktioniert, wenn the_function
Vorlage ist, ist das Folgende eine Problemumgehung:
#include <tuple>
template <typename T, typename U> void my_func(T &&t, U &&u) {}
int main(int argc, char *argv[argc]) {
std::tuple<int, float> my_tuple;
std::apply([](auto &&... args) { my_func(args...); }, my_tuple);
return 0;
}
Diese Problemumgehung ist eine vereinfachte Lösung für das allgemeine Problem des Übergebens von Überladungssätzen und Funktionsvorlagen, wo eine Funktion erwartet wird. Die allgemeine Lösung (eine, die sich um Perfect-Forwarding, Constexpr-ness und Noexcept-ness kümmert) wird hier vorgestellt: https://blog.tartanllama.xyz/passing-overload-sets/.

David
Hier ist mein Code, falls es jemanden interessiert
Grundsätzlich entrollt der Compiler zur Kompilierzeit alle Argumente rekursiv in verschiedenen inklusiven Funktionsaufrufen -> Aufrufe -> Aufrufe … -> Aufrufe <0>, was der letzte ist, und der Compiler optimiert weg die verschiedenen Zwischenfunktionsaufrufe, um nur den letzten zu behalten, der das Äquivalent von func(arg1, arg2, arg3, …)
Es werden 2 Versionen bereitgestellt, eine für eine Funktion, die von einem Objekt aufgerufen wird, und die andere für eine statische Funktion.
#include <tr1/tuple>
/**
* Object Function Tuple Argument Unpacking
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @tparam N Number of tuple arguments to unroll
*
* @ingroup g_util_tuple
*/
template < uint N >
struct apply_obj_func
{
template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( T* pObj,
void (T::*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& t,
Args... args )
{
apply_obj_func<N-1>::applyTuple( pObj, f, t, std::tr1::get<N-1>( t ), args... );
}
};
//-----------------------------------------------------------------------------
/**
* Object Function Tuple Argument Unpacking End Point
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @ingroup g_util_tuple
*/
template <>
struct apply_obj_func<0>
{
template < typename T, typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( T* pObj,
void (T::*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& /* t */,
Args... args )
{
(pObj->*f)( args... );
}
};
//-----------------------------------------------------------------------------
/**
* Object Function Call Forwarding Using Tuple Pack Parameters
*/
// Actual apply function
template < typename T, typename... ArgsF, typename... ArgsT >
void applyTuple( T* pObj,
void (T::*f)( ArgsF... ),
std::tr1::tuple<ArgsT...> const& t )
{
apply_obj_func<sizeof...(ArgsT)>::applyTuple( pObj, f, t );
}
//-----------------------------------------------------------------------------
/**
* Static Function Tuple Argument Unpacking
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @tparam N Number of tuple arguments to unroll
*
* @ingroup g_util_tuple
*/
template < uint N >
struct apply_func
{
template < typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( void (*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& t,
Args... args )
{
apply_func<N-1>::applyTuple( f, t, std::tr1::get<N-1>( t ), args... );
}
};
//-----------------------------------------------------------------------------
/**
* Static Function Tuple Argument Unpacking End Point
*
* This recursive template unpacks the tuple parameters into
* variadic template arguments until we reach the count of 0 where the function
* is called with the correct parameters
*
* @ingroup g_util_tuple
*/
template <>
struct apply_func<0>
{
template < typename... ArgsF, typename... ArgsT, typename... Args >
static void applyTuple( void (*f)( ArgsF... ),
const std::tr1::tuple<ArgsT...>& /* t */,
Args... args )
{
f( args... );
}
};
//-----------------------------------------------------------------------------
/**
* Static Function Call Forwarding Using Tuple Pack Parameters
*/
// Actual apply function
template < typename... ArgsF, typename... ArgsT >
void applyTuple( void (*f)(ArgsF...),
std::tr1::tuple<ArgsT...> const& t )
{
apply_func<sizeof...(ArgsT)>::applyTuple( f, t );
}
// ***************************************
// Usage
// ***************************************
template < typename T, typename... Args >
class Message : public IMessage
{
typedef void (T::*F)( Args... args );
public:
Message( const std::string& name,
T& obj,
F pFunc,
Args... args );
private:
virtual void doDispatch( );
T* pObj_;
F pFunc_;
std::tr1::tuple<Args...> args_;
};
//-----------------------------------------------------------------------------
template < typename T, typename... Args >
Message<T, Args...>::Message( const std::string& name,
T& obj,
F pFunc,
Args... args )
: IMessage( name ),
pObj_( &obj ),
pFunc_( pFunc ),
args_( std::forward<Args>(args)... )
{
}
//-----------------------------------------------------------------------------
template < typename T, typename... Args >
void Message<T, Args...>::doDispatch( )
{
try
{
applyTuple( pObj_, pFunc_, args_ );
}
catch ( std::exception& e )
{
}
}

sigidagi
In C++ gibt es viele Möglichkeiten, Tupel zu erweitern/entpacken und diese Tupelelemente auf eine variadische Vorlagenfunktion anzuwenden. Hier ist eine kleine Hilfsklasse, die ein Indexarray erstellt. Es wird häufig in der Template-Metaprogrammierung verwendet:
// ------------- UTILITY---------------
template<int...> struct index_tuple{};
template<int I, typename IndexTuple, typename... Types>
struct make_indexes_impl;
template<int I, int... Indexes, typename T, typename ... Types>
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...>
{
typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type;
};
template<int I, int... Indexes>
struct make_indexes_impl<I, index_tuple<Indexes...> >
{
typedef index_tuple<Indexes...> type;
};
template<typename ... Types>
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...>
{};
Jetzt ist der Code, der die Arbeit erledigt, nicht so groß:
// ----------UNPACK TUPLE AND APPLY TO FUNCTION ---------
#include <tuple>
#include <iostream>
using namespace std;
template<class Ret, class... Args, int... Indexes >
Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup)
{
return pf( forward<Args>( get<Indexes>(tup))... );
}
template<class Ret, class ... Args>
Ret apply(Ret (*pf)(Args...), const tuple<Args...>& tup)
{
return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup));
}
template<class Ret, class ... Args>
Ret apply(Ret (*pf)(Args...), tuple<Args...>&& tup)
{
return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup));
}
Der Test ist unten dargestellt:
// --------------------- TEST ------------------
void one(int i, double d)
{
std::cout << "function one(" << i << ", " << d << ");\n";
}
int two(int i)
{
std::cout << "function two(" << i << ");\n";
return i;
}
int main()
{
std::tuple<int, double> tup(23, 4.5);
apply(one, tup);
int d = apply(two, std::make_tuple(2));
return 0;
}
Ich bin kein großer Experte für andere Sprachen, aber ich denke, wenn diese Sprachen keine solche Funktionalität in ihrem Menü haben, gibt es keine Möglichkeit, dies zu tun. Zumindest mit C++ können Sie das, und ich denke, es ist nicht so kompliziert …
Das finde ich die eleganteste Lösung (und wird optimal weitergeleitet):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T && t, A &&... a)
-> decltype(Apply<N-1>::apply(
::std::forward<F>(f), ::std::forward<T>
::std::get<N-1>(::std::forward<T>
))
{
return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>
::std::get<N-1>(::std::forward<T>
);
}
};
template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T &&, A &&... a)
-> decltype(::std::forward<F>(f)(::std::forward<A>(a)...))
{
return ::std::forward<F>(f)(::std::forward<A>(a)...);
}
};
template<typename F, typename T>
inline auto apply(F && f, T && t)
-> decltype(Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), ::std::forward<T>
{
return Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), ::std::forward<T>
}
Beispielnutzung:
void foo(int i, bool b);
std::tuple<int, bool> t = make_tuple(20, false);
void m()
{
apply(&foo, t);
}
Leider kann GCC (mindestens 4.6) dies nicht mit “Entschuldigung, nicht implementiert: Mangelüberladung” kompilieren (was einfach bedeutet, dass der Compiler die C++ 11-Spezifikation noch nicht vollständig implementiert), und da er verschiedene Vorlagen verwendet, wird er dies nicht tun Arbeit in MSVC, also ist es mehr oder weniger nutzlos. Sobald es jedoch einen Compiler gibt, der die Spezifikation unterstützt, ist dies IMHO der beste Ansatz. (Hinweis: Es ist nicht so schwer, dies so zu ändern, dass Sie die Mängel in GCC umgehen oder mit Boost Preprocessor implementieren können, aber es ruiniert die Eleganz, also poste ich diese Version.)
GCC 4.7 unterstützt diesen Code jetzt problemlos.
Bearbeiten: Um den eigentlichen Funktionsaufruf herum hinzugefügt, um das Rvalue-Referenzformular zu unterstützen * dies, falls Sie Clang verwenden (oder wenn jemand anderes tatsächlich dazu kommt, es hinzuzufügen).
Bearbeiten: Fehlendes Vorwärts um das Funktionsobjekt im Körper der Nicht-Member-Apply-Funktion hinzugefügt. Danke an pheedbaq für den Hinweis, dass es fehlte.
Bearbeiten: Und hier ist die C ++ 14-Version, nur weil sie so viel schöner ist (noch nicht wirklich kompiliert):
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template<size_t N>
struct Apply {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T && t, A &&... a) {
return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>
::std::get<N-1>(::std::forward<T>
);
}
};
template<>
struct Apply<0> {
template<typename F, typename T, typename... A>
static inline auto apply(F && f, T &&, A &&... a) {
return ::std::forward<F>(f)(::std::forward<A>(a)...);
}
};
template<typename F, typename T>
inline auto apply(F && f, T && t) {
return Apply< ::std::tuple_size< ::std::decay_t<T>
>::value>::apply(::std::forward<F>(f), ::std::forward<T>
}
Hier ist eine Version für Member-Funktionen (nicht sehr viel getestet!):
using std::forward; // You can change this if you like unreadable code or care hugely about namespace pollution.
template<size_t N>
struct ApplyMember
{
template<typename C, typename F, typename T, typename... A>
static inline auto apply(C&& c, F&& f, T&& t, A&&... a) ->
decltype(ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>
{
return ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>
}
};
template<>
struct ApplyMember<0>
{
template<typename C, typename F, typename T, typename... A>
static inline auto apply(C&& c, F&& f, T&&, A&&... a) ->
decltype((forward<C>(c)->*forward<F>(f))(forward<A>(a)...))
{
return (forward<C>(c)->*forward<F>(f))(forward<A>(a)...);
}
};
// C is the class, F is the member function, T is the tuple.
template<typename C, typename F, typename T>
inline auto apply(C&& c, F&& f, T&& t) ->
decltype(ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>
{
return ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>
}
// Example:
class MyClass
{
public:
void foo(int i, bool b);
};
MyClass mc;
std::tuple<int, bool> t = make_tuple(20, false);
void m()
{
apply(&mc, &MyClass::foo, t);
}

Peter Som
template<typename F, typename Tuple, std::size_t ... I>
auto apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>
}
template<typename F, typename Tuple>
auto apply(F&& f, Tuple&& t) {
using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>
}
Dies wird mithilfe von index_sequence aus dem C++14-Entwurf übernommen. Ich könnte vorschlagen, sie in einem zukünftigen Standard (TS) anwenden zu lassen.

Gemeinschaft
All diese Implementierungen sind gut. Aber aufgrund der Verwendung eines Zeigers auf den Member-Funktions-Compiler kann der Zielfunktionsaufruf oft nicht inline sein (zumindest kann gcc 4.8 das nicht, egal was, warum gcc keine Funktionszeiger inline kann, die bestimmt werden können?)
Aber die Dinge ändern sich, wenn der Zeiger an die Member-Funktion als Template-Argumente gesendet wird, nicht als Funktionsparameter:
/// from https://stackoverflow.com/a/9288547/1559666
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };
template<typename TT>
using makeSeq = typename gens< std::tuple_size< typename std::decay<TT>::type >::value >::type;
// deduce function return type
template<class ...Args>
struct fn_type;
template<class ...Args>
struct fn_type< std::tuple<Args...> >{
// will not be called
template<class Self, class Fn>
static auto type_helper(Self &self, Fn f) -> decltype((self.*f)(declval<Args>()...)){
//return (self.*f)(Args()...);
return NULL;
}
};
template<class Self, class ...Args>
struct APPLY_TUPLE{};
template<class Self, class ...Args>
struct APPLY_TUPLE<Self, std::tuple<Args...>>{
Self &self;
APPLY_TUPLE(Self &self): self(self){}
template<class T, T (Self::* f)(Args...), class Tuple>
void delayed_call(Tuple &&list){
caller<T, f, Tuple >(forward<Tuple>(list), makeSeq<Tuple>() );
}
template<class T, T (Self::* f)(Args...), class Tuple, int ...S>
void caller(Tuple &&list, const seq<S...>){
(self.*f)( std::get<S>(forward<Tuple>(list))... );
}
};
#define type_of(val) typename decay<decltype(val)>::type
#define apply_tuple(obj, fname, tuple) \
APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
&decay<decltype(obj)>::type::fname \
> \
(tuple);
Und Verwendung:
struct DelayedCall
{
void call_me(int a, int b, int c){
std::cout << a+b+c;
}
void fire(){
tuple<int,int,int> list = make_tuple(1,2,3);
apply_tuple(*this, call_me, list); // even simpler than previous implementations
}
};
Beweis für Inlinable http://goo.gl/5UqVnC
Mit kleinen Änderungen können wir „überladen“ apply_tuple
:
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define VARARG_IMPL_(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL_(base, count, __VA_ARGS__)
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define apply_tuple2(fname, tuple) apply_tuple3(*this, fname, tuple)
#define apply_tuple3(obj, fname, tuple) \
APPLY_TUPLE<typename decay<decltype(obj)>::type, typename decay<decltype(tuple)>::type >(obj).delayed_call< \
decltype( fn_type< type_of(tuple) >::type_helper(obj, &decay<decltype(obj)>::type::fname) ), \
&decay<decltype(obj)>::type::fname \
/* ,decltype(tuple) */> \
(tuple);
#define apply_tuple(...) VARARG(apply_tuple, __VA_ARGS__)
...
apply_tuple(obj, call_me, list);
apply_tuple(call_me, list); // call this->call_me(list....)
Außerdem ist dies die einzige Lösung, die mit Vorlagenfunktionen arbeitet.
1) Wenn Sie eine fertige parameter_pack-Struktur als Funktionsargument haben, können Sie std::tie einfach so verwenden:
template <class... Args>
void tie_func(std::tuple<Args...> t, Args&... args)
{
std::tie<Args...>(args...) = t;
}
int main()
{
std::tuple<int, double, std::string> t(2, 3.3, "abc");
int i;
double d;
std::string s;
tie_func(t, i, d, s);
std::cout << i << " " << d << " " << s << std::endl;
}
2) Wenn Sie kein fertiges Parampack arg haben, müssen Sie das Tupel wie folgt abwickeln
#include <tuple>
#include <functional>
#include <iostream>
template<int N>
struct apply_wrap {
template<typename R, typename... TupleArgs, typename... UnpackedArgs>
static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>& t, UnpackedArgs... args )
{
return apply_wrap<N-1>::applyTuple( f, t, std::get<N-1>( t ), args... );
}
};
template<>
struct apply_wrap<0>
{
template<typename R, typename... TupleArgs, typename... UnpackedArgs>
static R applyTuple( std::function<R(TupleArgs...)>& f, const std::tuple<TupleArgs...>&, UnpackedArgs... args )
{
return f( args... );
}
};
template<typename R, typename... TupleArgs>
R applyTuple( std::function<R(TupleArgs...)>& f, std::tuple<TupleArgs...> const& t )
{
return apply_wrap<sizeof...(TupleArgs)>::applyTuple( f, t );
}
int fac(int n)
{
int r=1;
for(int i=2; i<=n; ++i)
r *= i;
return r;
}
int main()
{
auto t = std::make_tuple(5);
auto f = std::function<decltype(fac)>(&fac);
cout << applyTuple(f, t);
}
9939700cookie-checkWie erweitere ich ein Tupel in die Argumente einer variadischen Vorlagenfunktion?yes
Der C++14-Standard hat eine Lösung, siehe; open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
– Skeen
15. Oktober 2013 um 21:34 Uhr
Die Idee ist, das Tupel in einer einzigen variadischen Explosion zu entpacken, indem es verwendet wird
integer_sequence
sehen en.cppreference.com/w/cpp/utility/integer_sequence– Skeen
15. Oktober 2013 um 21:36 Uhr
Mit einem
integer_sequence S
rufen Sie Ihre Funktion einfach als auffunc(std::get<S>(tuple)...)
und lassen Sie den Compiler den Rest erledigen.– Skeen
15. Oktober 2013 um 21:39 Uhr
Wenn Sie C++17 oder höher verwenden, ignorieren Sie diese Antwort und sehen Sie sich die folgende mit std::apply an
– lewis
30. April 2019 um 15:00 Uhr