튜플을 가변 템플릿 함수의 인수로 어떻게 확장합니까?


135

가변 템플릿 인수가있는 템플릿 함수의 경우를 고려하십시오.

template<typename Tret, typename... T> Tret func(const T&... t);

이제 튜플 t값이 있습니다. func()튜플 값을 인수로 사용하여 어떻게 호출 합니까? bind()함수와 call()함수 및 apply()현재 사용되지 않는 다른 문서 의 함수에 대해 읽었습니다 . GNU GCC 4.4 구현은 클래스에 call()기능 이있는 것으로 보이지만 bind()주제에 대한 문서는 거의 없습니다.

어떤 사람들은 손으로 쓴 재귀 해킹을 제안하지만, 가변적 인 템플릿 인수의 진정한 가치는 위와 같은 경우에 사용할 수 있다는 것입니다.

누구든지 해결책을 찾거나 어디서 읽을 수 있습니까?


5
C ++ 14 표준에는 해결책이 있습니다. open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html
Skeen

1
아이디어는 사용하여 단일 가변 폭발 튜플 압축을 해제하는 것입니다 integer_sequence, 참조 en.cppreference.com/w/cpp/utility/integer_sequence
Skeen

6
가 있으면 integer_sequence S함수를로 호출 func(std::get<S>(tuple)...)하고 컴파일러가 나머지를 처리하게하십시오.
Skeen

1
++ 17 이상을 C를 사용하는 경우,이 대답을 무시하고 표준을 사용하여 아래의 하나를 참조 :: 적용
루이스

답변:


46

누군가 관심이 있다면 내 코드는 다음과 같습니다.

기본적으로 컴파일 타임에 컴파일러는 다양한 포괄 함수 호출 <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 )
  {

  }
}

2
문제의 "함수"가 실제로 생성자 인 경우 작동하도록 이것을 적용 할 수 있습니까?
HighCommander4

당신이하고 싶은 일의 예를 제공해 주시면 거기서 갈 수 있습니다.
David

이 솔루션은 컴파일 시간 오버 헤드 만 제공하며 결국 (pObj-> * f) (arg0, arg, 1, ... argN); 권리?
Goofy

예, 컴파일러는 여러 함수 호출을 최종 메타 데이터로 압축합니다. 마치이 메타 프로그래밍의 장점 인 직접 작성한 것처럼 말입니다.
David

tr1c ++ 11로 모든 것들을 지금 가져올 수 있습니다
Ryan Haining

37

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를 관리하는 솔루션)이 제공됩니다 .


std :: apply 의 예제 코드에 따르면 the_function템플릿이 있으면 작동하지 않는 것 같습니다 .
Zitrax

1
@Zitrax 함수의 템플릿 인수를 지정할 수 있습니다.std::apply(add_generic<float>, std::make_pair(2.0f, 3.0f));
Erbureth는 Reinstate Monica가

이것은 가장 단순하고 가장 우아한 솔루션입니다. 그리고 그것은 놀라운 일을합니다. 정말 고마워요, M. Alaggan !!!!!! +100 투표
엘리엇

36

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 ++을 사용하면 할 수 있으며 그렇게 복잡하지는 않다고 생각합니다 ...


"... 그리고 튜플 요소를 가변 템플릿 함수에 적용하십시오" . 테스트 섹션에는 템플릿이 아닌 가변 함수 만 포함됩니다. 내가 좋아하는 것을 추가 template<class ... T> void three(T...) {}하고 적용하려고하면 컴파일되지 않습니다.
Zitrax

32

나는 이것이 가장 우아한 해결책이라고 생각합니다 (그리고 최적으로 전달됩니다).

#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);
}

1
나열된 답변 중 +1, 당신의 인수는 벡터 인 인수를 사용하여 얻을 수있는 가장 가까운 것입니다 ...하지만 여전히 컴파일 오류가 발생합니다. ideone.com/xH5kBH -DDIRECT_CALL 을 사용하여이를 컴파일하고 실행하면 출력 결과를 확인할 수 있습니다. 그렇지 않으면 gcc 4.7.2와 함께 컴파일 오류가 발생합니다 (decltype이 특별한 경우를 알아낼만큼 똑똑하지 않다고 생각합니다).
kfmfe04

3
ideaone의 gcc 버전은 이전 버전으로, 맹 글링 된 decltype 반환 유형 오버로드를 지원하지 않습니다. gcc 4.7.2 에서이 코드를 비교적 철저히 테스트했으며 아무런 문제가 없습니다. gcc 4.8에서는 새로운 C ++ 17 자동 반환 값 기능을 사용하여 모든 비정상적인 decltype 후행 반환 유형을 피할 수 있습니다.
DRayX

1
비회원 apply기능 에서 호기심 으로 인해 반환 유형 f과 마찬가지로 std::forward호출로 래핑되지 않는 이유는 무엇입니까? 필요하지 않습니까?
Brett Rossier

3
호기심으로 GCC 4.8에서 컴파일을 시도하고 -O0 이외의 최적화 수준과 foo('x', true)동일한 어셈블리 코드로 컴파일했습니다 apply(foo, ::std::make_tuple('x', true)).
DRayX

2
C ++ 14 integer_sequence를 사용하면 apply()예제에서 거의 올바른 구현을 얻을 수 있습니다. 아래 내 답변을 참조하십시오.
PeterSom

28
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)에 적용 할 것을 제안 할 수 있습니다.


1

뉴스가 안 좋아 보인다.

방금 출시 된 초안 표준 을 읽은 후에는 이에 대한 기본 제공 솔루션이 보이지 않습니다.

그러한 것들에 대해 물어 보는 가장 좋은 장소는 comp.lang.c ++. moderated입니다. 일부 사람들은 정기적으로 표준 게시물을 작성하는 데 관여하기 때문입니다.

이 스레드 를 체크 아웃 하면 누군가가 같은 질문을 할 수 있습니다 (아마도이 ​​대답은 약간 실망 스럽습니다!). 그러나 엉덩이가 못생긴 구현이 제안됩니다.

tuple그 방법으로 변환하는 것이 더 쉽기 때문에 함수가을 받아들이는 것이 더 간단한 지 궁금했습니다 . 그러나 이것은 모든 함수가 최대한의 유연성을 위해 튜플을 인수로 받아 들여야 함을 의미하므로 함수 팩에 튜플이 기본적으로 확장되어 제공되지 않는 기묘함을 보여줍니다.

업데이트 : 위의 링크가 작동하지 않습니다-붙여 넣기를 시도하십시오.

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/750fa3815cdaac45/d8dc09e34bbb9661?lnk=gst&q=tuple+variadic#d8dc09e34bbb9661


왜 그들이 튜플과 함수 인수 팩에 대한 별도의 개념을 갖는지 궁금합니다. 어쩌면 호환되는 컴파일러에서는 서로 호환 가능하지만 내가 읽은 곳의 표시는 발견하지 못했습니다.
Daniel Earwicker 2016 년

2
tuple <int, char, string>이 ​​별도의 유형으로 필요하기 때문에; 모든 호출 중에 make_type이 필요없는 함수를 만드는 기능도 마찬가지입니다.
coppro 2009

1
또한 가장 좋은 장소는 comp.lang.c ++. moderated가 아닙니다. C ++ 1x에 대한 질문은 거의 항상 comp.std.c ++로 향하는 것이 좋습니다.
coppro 2009

1

이 모든 구현이 좋습니다. 그러나 멤버 함수 컴파일러에 대한 포인터를 사용하면 대상 함수 호출을 인라인 할 수없는 경우가 있습니다 ( 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....)

또한 이것은 템플릿 기능으로 작동하는 유일한 솔루션입니다.


1

1) readymade parameter_pack 구조체를 함수 인수로 가지고 있다면 std :: tie를 다음과 같이 사용할 수 있습니다.

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) 기성품 parampack 인수가없는 경우 다음과 같이 튜플을 풀어야합니다

#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);
}

0

이건 어때요:

// Warning: NOT tested!
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>

using std::declval;
using std::forward;
using std::get;
using std::integral_constant;
using std::size_t;
using std::tuple;

namespace detail
{
    template < typename Func, typename ...T, typename ...Args >
    auto  explode_tuple( integral_constant<size_t, 0u>, tuple<T...> const &t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    { return forward<Func>( f )( forward<Args>(a)... ); }

    template < size_t Index, typename Func, typename ...T, typename ...Args >
    auto  explode_tuple( integral_constant<size_t, Index>, tuple<T...> const&t,
     Func &&f, Args &&...a )
     -> decltype( forward<Func>(f)(declval<T const>()...) )
    {
        return explode_tuple( integral_constant<size_t, Index - 1u>{}, t,
         forward<Func>(f), get<Index - 1u>(t), forward<Args>(a)... );
    }
}

template < typename Func, typename ...T >
auto  run_tuple( Func &&f, tuple<T...> const &t )
 -> decltype( forward<Func>(f)(declval<T const>()...) )
{
    return detail::explode_tuple( integral_constant<size_t, sizeof...(T)>{}, t,
     forward<Func>(f) );
}

template < typename Tret, typename ...T >
Tret  func_T( tuple<T...> const &t )
{ return run_tuple( &func<Tret, T...>, t ); }

run_tuple함수 템플릿은 주어진 튜플 오며 제공된 기능에 개별적 요소를 통과한다. 헬퍼 함수 템플릿을 재귀 적으로 호출하여 작업을 수행합니다 explode_tuple. run_tuple튜플의 크기를 전달하는 것이 중요합니다 explode_tuple. 이 숫자는 추출 할 요소 수에 대한 카운터 역할을합니다.

튜플이 비어 있으면 원격 함수를 다른 인수로 사용하여 run_tuple의 첫 번째 버전 을 호출합니다 explode_tuple. 원격 함수는 인수없이 호출되며 완료되었습니다. 튜플이 비어 있지 않으면 explode_tuple원격 기능과 함께 더 높은 숫자가의 두 번째 버전으로 전달됩니다 . 재귀 호출explode_tuple카운터 번호가 1 씩 감소하고 마지막 튜플 요소에 대한 참조가 원격 함수 이후의 인수로 고정된다는 점을 제외하고는 동일한 인수로 구성됩니다. 재귀 호출에서 카운터는 0이 아니며 카운터가 다시 감소 된 상태에서 다른 호출이 수행되고 다음 참조되지 않은 요소가 원격 함수 다음에 삽입 된 다른 인수 앞에 또는 인수에 도달하기 전에 인수 목록에 삽입됩니다. 0이고 원격 함수는 그 이후에 누적 된 모든 인수 와 함께 호출됩니다 .

특정 버전의 함수 템플릿을 강제로 적용하는 구문이 있는지 확실하지 않습니다. 포인터 대 함수를 함수 객체로 사용할 수 있다고 생각합니다. 컴파일러가 자동으로 수정합니다.


0

MSVS 2013RC를 평가 중이며 여기에 제안 된 이전 솔루션 중 일부를 컴파일하지 못했습니다. 예를 들어, MSVS는 네임 스페이스 제한 제한으로 인해 함수 매개 변수가 너무 많으면 "자동"반환 값을 컴파일하지 못합니다 (정보를 Microsoft에 보내서 수정하도록 함). 다른 경우에는 lamda로도 수행 할 수 있지만 함수의 리턴에 액세스해야합니다. 다음 두 예제는 동일한 결과를 제공합니다.

apply_tuple([&ret1](double a){ret1 = cos(a); }, std::make_tuple<double>(.2));
ret2 = apply_tuple((double(*)(double))cos, std::make_tuple<double>(.2));

그리고 나보다 먼저 여기에 답변을 게시 한 사람들에게 감사드립니다. 그렇지 않으면 이것에 도달하지 못했을 것입니다.

template<size_t N>
struct apply_impl {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_tuple(std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype(apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return apply_impl<N-1>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t),
                          std::get<N-1>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};

// This is a work-around for MSVS 2013RC that is required in some cases
#if _MSC_VER <= 1800 /* update this when bug is corrected */
template<>
struct apply_impl<6> {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(F&& f, T&& t, A&&... a)
    -> decltype(std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return std::forward<F>(f)(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
    template<typename C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&& t, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::get<0>(std::forward<T>(t)), std::get<1>(std::forward<T>(t)), std::get<2>(std::forward<T>(t)),
           std::get<3>(std::forward<T>(t)), std::get<4>(std::forward<T>(t)), std::get<5>(std::forward<T>(t)), std::forward<A>(a)...);
    }
};
#endif

template<>
struct apply_impl<0> {
    template<typename F, typename T, typename... A>
    static inline auto apply_tuple(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 C, typename F, typename T, typename... A>
    static inline auto apply_tuple(C*const o, F&& f, T&&, A&&... a)
    -> decltype((o->*std::forward<F>(f))(std::forward<A>(a)...)) {
         return (o->*std::forward<F>(f))(std::forward<A>(a)...);
    }
};

// Apply tuple parameters on a non-member or static-member function by perfect forwarding
template<typename F, typename T>
inline auto apply_tuple(F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(std::forward<F>(f), std::forward<T>(t));
}

// Apply tuple parameters on a member function
template<typename C, typename F, typename T>
inline auto apply_tuple(C*const o, F&& f, T&& t)
-> decltype(apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t))) {
     return apply_impl<std::tuple_size<typename std::decay<T>::type>::value>::apply_tuple(o, std::forward<F>(f), std::forward<T>(t));
}

왜 객체 인수를 const 포인터로 만드나요? 포인터가 아닌 참조가 아닌 const 참조입니까? 호출 가능 함수가 그렇지 않으면 const어떻게됩니까?
tower120

0

@David의 솔루션을 확장하여 재귀 템플릿을 작성할 수 있습니다.

  1. (과도한 상세, imo) integer_sequence의미를 사용하지 않습니다
  2. int N재귀 반복 계산에 추가 임시 템플릿 매개 변수 를 사용하지 않습니다.
  3. (정적 / 전역 functors의 경우 선택 사항) functor를 컴파일 타임 최적화를위한 템플릿 매개 변수로 사용합니다.

예 :

template <class F, F func>
struct static_functor {
    template <class... T, class... Args_tmp>
    static inline auto apply(const std::tuple<T...>& t, Args_tmp... args)
            -> decltype(func(std::declval<T>()...)) {
        return static_functor<F,func>::apply(t, args...,
                std::get<sizeof...(Args_tmp)>(t));
    }
    template <class... T>
    static inline auto apply(const std::tuple<T...>& t, T... args)
            -> decltype(func(args...)) {
        return func(args...);
    }
};

static_functor<decltype(&myFunc), &myFunc>::apply(my_tuple);

또는 functor가 컴파일 타임에 정의되지 않은 경우 (예 : constexprfunctor 가 아닌 인스턴스 또는 람다 식)이를 클래스 템플릿 매개 변수 대신 함수 매개 변수로 사용하고 실제로 포함하는 클래스를 완전히 제거 할 수 있습니다.

template <class F, class... T, class... Args_tmp>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
        Args_tmp... args) -> decltype(func(std::declval<T>()...)) {
    return apply_functor(func, t, args..., std::get<sizeof...(Args_tmp)>(t));
}
template <class F, class... T>
inline auto apply_functor(F&& func, const std::tuple<T...>& t,
        T... args) -> decltype(func(args...)) {
    return func(args...);
}

apply_functor(&myFunc, my_tuple);

포인터 대 멤버 함수 호출 가능 항목의 경우 @David의 답변과 유사하게 위 코드 조각 중 하나를 조정할 수 있습니다.

설명

두 번째 코드와 관련하여 두 가지 템플릿 함수가 있습니다. 첫 번째 함수는 functor func, tuple twith types T...argstype of parameter pack 을 사용합니다 Args_tmp.... 호출되면 t시작 ( 0)에서 끝까지 한 번에 하나씩 객체를 매개 변수 팩에 재귀 적으로 추가 하고 새로운 증분 된 매개 변수 팩으로 함수를 다시 호출합니다.

두 번째 함수의 시그니처는 T...parameter pack 유형 을 사용한다는 점을 제외하고 첫 번째 함수와 거의 동일합니다 args. 따라서 args첫 번째 함수에서의 값으로 완전히 채워지면 t유형은 T...(psuedo-code로 typeid(T...) == typeid(Args_tmp...)) 형식이 되므로 컴파일러는 대신 두 번째 오버로드 된 함수를 호출하여 호출합니다 func(args...).

정적 functor 예제의 코드는 functor를 대신 클래스 템플릿 인수로 사용하여 동일하게 작동합니다.


첫 번째 옵션의 컴파일 타임 최적화에 대한 의견을 주시면 답변을보다 완벽하게 만들 수 있습니다 (아마도 새로운 것을 배울 수 있음).
CrepeGoat

-3

variadic 인수를 튜플 클래스로 래핑 한 다음 컴파일 시간 재귀 ( link 참조 )를 사용하여 관심있는 인덱스를 검색하십시오. variadic 템플릿을 컨테이너 또는 컬렉션으로 압축 해제하면 유형이 다른 유형의 유형이 아닐 수 있습니다

template<typename... Args>
auto get_args_as_tuple(Args... args) -> std::tuple<Args...> 
{
    return std::make_tuple(args);
}

6
문제는 다른 방법이었습니다. 하지 Args...-> tuple하지만 tuple-> Args....
Xeo

-4

이 간단한 솔루션은 저에게 효과적입니다.

template<typename... T>
void unwrap_tuple(std::tuple<T...>* tp)
{
    std::cout << "And here I have the tuple types, all " << sizeof...(T) << " of them" << std::endl;
}

int main()
{
    using TupleType = std::tuple<int, float, std::string, void*>;

    unwrap_tuple((TupleType*)nullptr); // trick compiler into using template param deduction
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.