2015-02-05 1 views
7

임의의 튜플의 각 요소에 대해 템플릿이나 오버로드 된 함수를 호출해야합니다. 정확히 말하면, 튜플에 지정된대로이 함수를 요소에 대해 호출해야합니다.std :: tuple의 요소에 func를 자연스러운 (반대가 아닌) 순서로 적용하기

예를 들면.

1; 2.0f; 

및 NOT 2.0f; 1;을 : 나는, Apply<Lambda, int, float>()(Lambda(), t)처럼라고하면 얻을 것이다 약간의 구조체/기능 Apply가 필요 튜플 std::tuple<int, float> t{1, 2.0f}; 및 기능

class Lambda{ 
public: 
    template<class T> 
    void operator()(T arg){ std::cout << arg << "; "; } 
}; 

있습니다.

"원시"매개 변수 팩이 함수에 전달되고 그 반대 순서로 튜플에 대해 수행하는 방법을 알고있는 경우 해결책을 알고 있음에 유의하십시오. 그러나 부분적으로 Apply을 전문으로 다음과 같은 시도가 실패 :

template<class Func, size_t index, class ...Components> 
class ForwardsApplicator{ 
public: 
    void operator()(Func func, const std::tuple<Components...>& t){ 
     func(std::get<index>(t)); 
     ForwardsApplicator<Func, index + 1, Components...>()(func, t); 
    } 
}; 

template<class Func, class... Components> 
class ForwardsApplicator < Func, sizeof...(Components), Components... > { 
public: 
    void operator()(Func func, const std::tuple<Components...>& t){} 
}; 

int main{ 
    ForwardsApplicator<Lambda, 0, int, float>()(Lambda{}, std::make_tuple(1, 2.0f)); 
} 

코드는 컴파일하지만 첫 번째 인수가 인쇄됩니다. 단지 내가 그렇게 어떻게 길이 2와 튜플에 대한, 물론,하지만 - - 나는

template<class Func, class... Components> 
class ForwardsApplicator < Func, 2, Components... >{...} 

ForwardsApplicator 전문화를 대체 할 경우, 그것은 제대로 작동 가능한 경우, 우아 - 임의의 길이의 튜플에 대해?

편집 : 답변 해 주셔서 감사합니다. 세 사람 모두 정말로 솔직하게 말하고 가능한 모든 유리한 지점에서 문제를 설명합니다.

답변

6

이다 꼬리 튜플 헤드를 처리하기위한 단순한 방법은 (꼬리 재귀 대조적으로) 헤드 재귀 인 알 수없는 형식 목록 Components. GCC는이 오류에 대해 불평합니다 :

prog.cpp:16:7: error: template argument 'sizeof... (Components)' involves template parameter(s) 
class ForwardsApplicator < Func, sizeof...(Components), Components... > { 
    ^

약간 다른 접근 방식을 제안합니다. 그런 다음

template<class ...Components> 
void operator()(Func func, const std::tuple<Components...>& t) { 
    ... 
} 

를 호출 순서를 반대로 : 먼저, 즉, operator()의 템플릿 매개 변수로 유형 목록 Components 이동 첫 번째 (즉, 마지막에 전화를 재귀 호출, index-1와 다음 invokation을 튜플 요소). 이 재귀를 index = sizeof...(Components)으로 시작하고 아무런 결과가없는 index = 0까지 진행하십시오. (따라서 전문화 과정은 0이고, 제가 말하기 시작한 문제인 sizeof...(Components)과는 무관합니다.)

템플릿 인수 공제하는 기능을 추가,이 전화를 돕는다 :

// General case (recursion) 
template<class Func, size_t index> 
class ForwardsApplicator{ 
public: 
    template<class ...Components> 
    void operator()(Func func, const std::tuple<Components...>& t){ 
     ForwardsApplicator<Func, index - 1>()(func, t); 
     func(std::get<index - 1>(t)); 
    } 
}; 

// Special case (stop recursion) 
template<class Func> 
class ForwardsApplicator<Func, 0> { 
public: 
    template<class ...Components> 
    void operator()(Func func, const std::tuple<Components...>& t){} 
}; 

// Helper function for template type deduction 
template<class Func, class ...Components> 
void apply(Func func, const std::tuple<Components...>& t) { 
    ForwardsApplicator<Func, sizeof...(Components)>()(func, t); 
} 

을 호출 할 다음 쉬운, 필요없이 전화 사이트에있는 모든 템플릿 매개 변수 :

apply(Lambda{}, std::make_tuple(1, 2.0f)); 

Live demo

+0

그 대답은 제가 찾고 있던 대답입니다. 우아하고 아주 마음에 품은 디자인에 가깝습니다. 감사! – Mischa

+1

특수 형식 템플릿 인수가 불법이라는 것이 맞습니다. 나는 OP가 VC12를 사용하고 있다고 생각합니다. VC12는 어떤 에러도 내지 않지만 좋은 것도하지 않습니다. 좋은 오류를 발행 VC14 CTP5에서 문제가 해결되었습니다. Clang (3.5.0 및 트렁크 228146)이 진단없이 코드를 컴파일하고 전문화와도 일치한다는 점은 주목할 가치가 있습니다. 이것은보고되어야합니다. – bogdan

2

템플릿에서 카운트 다운이 동일한 순서로 튜플 요소를 처리한다는 것을 의미하지는 않습니다.

#include <tuple> 
#include <iostream> 

// Our entry point is the tuple size 
template<typename Tuple, std::size_t i = std::tuple_size<Tuple>::value> 
struct tuple_applicator { 
    template<typename Func> static void apply(Tuple &t, Func &&f) { 
    // but we recurse before we process the element associated with that 
    // number, reversing the order so that the front elements are processed 
    // first. 
    tuple_applicator<Tuple, i - 1>::apply(t, std::forward<Func>(f)); 
    std::forward<Func>(f)(std::get<i - 1>(t)); 
    } 
}; 

// The recursion stops here. 
template<typename Tuple> 
struct tuple_applicator<Tuple, 0> { 
    template<typename Func> static void apply(Tuple &, Func &&) { } 
}; 

// and this is syntactical sugar 
template<typename Tuple, typename Func> 
void tuple_apply(Tuple &t, Func &&f) { 
    tuple_applicator<Tuple>::apply(t, std::forward<Func>(f)); 
} 

int main() { 
    std::tuple<int, double> t { 1, 2.3 }; 

    // The generic lambda requires C++14, the rest 
    // works with C++11 as well. Put your Lambda here instead. 
    tuple_apply(t, [](auto x) { std::cout << x * 2 << '\n'; }); 
} 

출력이 문제 size...(Components)가 특성화에 사용될 수 없다는 것이다

2 
4.6 
9

트릭 용 integer_sequence의 교과서입니다.

template<class Func, class Tuple, size_t...Is> 
void for_each_in_tuple(Func f, Tuple&& tuple, std::index_sequence<Is...>){ 
    using expander = int[]; 
    (void)expander { 0, ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... }; 
} 

template<class Func, class Tuple> 
void for_each_in_tuple(Func f, Tuple&& tuple){ 
    for_each_in_tuple(f, std::forward<Tuple>(tuple), 
       std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()); 
} 

Demo.

std::index_sequence 친구는 C++ 14이지만 순수한 라이브러리 확장이며 C++ 11에서 쉽게 구현할 수 있습니다. SO에 대한 6 가지 구현을 쉽게 찾을 수 있습니다.

관련 문제