가변 템플릿 인수가있는 템플릿 함수의 경우를 고려하십시오.
template<typename Tret, typename... T> Tret func(const T&... t);
이제 튜플 t
값이 있습니다. func()
튜플 값을 인수로 사용하여 어떻게 호출 합니까? bind()
함수와 call()
함수 및 apply()
현재 사용되지 않는 다른 문서 의 함수에 대해 읽었습니다 . GNU GCC 4.4 구현은 클래스에 call()
기능 이있는 것으로 보이지만 bind()
주제에 대한 문서는 거의 없습니다.
어떤 사람들은 손으로 쓴 재귀 해킹을 제안하지만, 가변적 인 템플릿 인수의 진정한 가치는 위와 같은 경우에 사용할 수 있다는 것입니다.
누구든지 해결책을 찾거나 어디서 읽을 수 있습니까?
답변
누군가 관심이 있다면 내 코드는 다음과 같습니다.
기본적으로 컴파일 타임에 컴파일러는 다양한 포괄 함수 호출 <N>-> 호출 <N-1>-> 호출 …-> 호출 <0>에서 모든 인수를 재귀 적으로 언 롤링합니다. func (arg1, arg2, arg3, …)와 동일한 마지막 함수 만 유지하기위한 다양한 중간 함수 호출
하나는 객체에서 호출되는 함수와 다른 하나는 정적 함수의 두 가지 버전으로 제공됩니다.
#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 )
{
}
}
답변
C ++ 17에서는 다음을 수행 할 수 있습니다.
std::apply(the_function, the_tuple);
이것은 std :: experimental :: apply를 사용하여 Clang ++ 3.9에서 이미 작동합니다.
the_function
템플릿으로 작성하면 작동하지 않는다는 의견에 응답하여 다음을 해결할 수 있습니다.
#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;
}
이 해결 방법은 함수가 예상되는 오버로드 세트 및 함수 템플릿을 전달하는 일반적인 문제에 대한 간단한 솔루션입니다. https://blog.tartanllama.xyz/passing-overload-sets/에서 일반적인 솔루션 (완벽한 전달, constexpr-ness 및 noexcept-ness를 관리하는 솔루션)이 제공됩니다 .
답변
C ++에는 튜플을 확장 / 포장 풀고 해당 튜플 요소를 가변 템플릿 함수에 적용하는 여러 가지 방법이 있습니다. 다음은 인덱스 배열을 만드는 작은 도우미 클래스입니다. 템플릿 메타 프로그래밍에서 많이 사용됩니다.
// ------------- 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...>
{};
이제 작업을 수행하는 코드는 그렇게 크지 않습니다.
// ----------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));
}
테스트는 다음과 같습니다.
// --------------------- 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;
}
나는 다른 언어의 큰 전문가는 아니지만, 이러한 언어가 메뉴에 그러한 기능을 가지고 있지 않으면 그렇게 할 수있는 방법이 없다고 생각합니다. 적어도 C ++을 사용하면 할 수 있으며 그렇게 복잡하지는 않다고 생각합니다 …
답변
나는 이것이 가장 우아한 해결책이라고 생각합니다 (그리고 최적으로 전달됩니다).
#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>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
))
{
return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
);
}
};
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>(t)))
{
return Apply< ::std::tuple_size<
typename ::std::decay<T>::type
>::value>::apply(::std::forward<F>(f), ::std::forward<T>(t));
}
사용법 예 :
void foo(int i, bool b);
std::tuple<int, bool> t = make_tuple(20, false);
void m()
{
apply(&foo, t);
}
불행히도 GCC (4.6 이상)는 “죄송하고 구현되지 않은 : mangling overload”(컴파일러가 아직 C ++ 11 사양을 완전히 구현하지 않았다는 의미)로 컴파일하지 못하며 가변 템플릿을 사용하기 때문에 MSVC에서 작동하므로 다소 쓸모가 없습니다. 그러나 사양을 지원하는 컴파일러가 있으면 IMHO에 가장 적합한 방법입니다. (참고 : GCC의 결함을 해결하거나 Boost Preprocessor로 구현할 수 있도록이를 수정하는 것은 어렵지 않지만 우아함을 망치므로 게시하는 버전입니다.)
GCC 4.7은 이제이 코드를 잘 지원합니다.
편집 : rvalue 참조 양식을 지원하기 위해 실제 함수 호출 주위에 앞으로 추가 * clang을 사용하는 경우 (또는 다른 사람이 실제로 추가하는 경우).
편집 : 비 멤버 적용 함수의 본문에서 함수 객체 주위에 누락이 추가되었습니다. 누락되었음을 지적한 pheedbaq에게 감사합니다.
편집 : 그리고 여기에 C ++ 14 버전이 훨씬 더 좋기 때문에 (실제로 컴파일하지는 않았습니다) :
#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>(t),
::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)...
);
}
};
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>(t));
}
다음은 멤버 함수용 버전입니다 (별로 테스트하지 않음).
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>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...))
{
return ApplyMember<N-1>::apply(forward<C>(c), forward<F>(f), forward<T>(t), std::get<N-1>(forward<T>(t)), forward<A>(a)...);
}
};
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>(t)))
{
return ApplyMember<std::tuple_size<typename std::decay<T>::type>::value>::apply(forward<C>(c), forward<F>(f), forward<T>(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);
}
답변
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>(t))...);
}
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>(t), Indices());
}
이것은 index_sequence를 사용하여 C ++ 14 초안에서 수정되었습니다. 향후 표준 (TS)에 적용 할 것을 제안 할 수 있습니다.
답변
뉴스가 안 좋아 보인다.
방금 출시 된 초안 표준 을 읽은 후에는 이에 대한 기본 제공 솔루션이 보이지 않습니다.
그러한 것들에 대해 물어 보는 가장 좋은 장소는 comp.lang.c ++. moderated입니다. 일부 사람들은 정기적으로 표준 게시물을 작성하는 데 관여하기 때문입니다.
이 스레드 를 체크 아웃 하면 누군가가 같은 질문을 할 수 있습니다 (아마도이 대답은 약간 실망 스럽습니다!). 그러나 엉덩이가 못생긴 구현이 제안됩니다.
tuple
그 방법으로 변환하는 것이 더 쉽기 때문에 함수가을 받아들이는 것이 더 간단한 지 궁금했습니다 . 그러나 이것은 모든 함수가 최대한의 유연성을 위해 튜플을 인수로 받아 들여야 함을 의미하므로 함수 팩에 튜플이 기본적으로 확장되어 제공되지 않는 기묘함을 보여줍니다.
업데이트 : 위의 링크가 작동하지 않습니다-붙여 넣기를 시도하십시오.
답변
이 모든 구현이 좋습니다. 그러나 멤버 함수 컴파일러에 대한 포인터를 사용하면 대상 함수 호출을 인라인 할 수없는 경우가 있습니다 ( gcc가 왜 결정 할 수있는 함수 포인터를 인라인 할 수 없는지에 관계없이 적어도 gcc 4.8은 할 수 없습니다 ).
그러나 멤버 함수에 대한 포인터를 함수 매개 변수가 아닌 템플릿 인수로 보내면 상황이 변경됩니다.
/// 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);
그리고 사용법 :
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
}
};
무적의 증거 http://goo.gl/5UqVnC
조금만 변경하면 “오버로드”할 수 있습니다 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....)
또한 이것은 템플릿 기능으로 작동하는 유일한 솔루션입니다.