2017-10-27 1 views
1

std::tuple의 각 요소에 대해 연산을 수행하는 함수를 작성하려고하지만 요소가 특정 유형 인 경우 하나의 작업 만 수행하도록 특수화했습니다. 다른 유형이라면 다른 일을 할 것입니다. 나는 모든 요소에 같은 일을 할 수있는 코드가 있습니다유형별로 튜플 요소에 대한 특수 함수를 만드는 방법

template<std::size_t I = 0, class ...Ts> 
inline typename std::enable_if<I == sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { } // base case: do nothing 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 

    auto el = std::get<I>(t); 
    // do thing with el 
    tupel_el_func<I+1, Ts...>(t); 
} 

을하지만 지금은 특정 유형 (예를 들어, 유형 char*)의있는 요소에 대해 다른 일을하기 위해이 기능을 전문화 할 필요가있다. 그래서 같은 std::enable_if를 사용하여 시도했지만 그것은 컴파일되지 않습니다 :

template<int N, typename... Ts> 
using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type; 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts) && std::is_same<char*, NthTypeOf<I, Ts...>>::value, void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 
    // function body for when get<I>(t) is a char* 
} 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts) && !std::is_same<char*, NthTypeOf<I, Ts...>>::value, void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 
    // function body for generic next element 
} 

이 도움을 주셔서 감사합니다.

당신은 각 유형에 대한 특별한 경우에 대한 논리를 구현 전문/일반 기능 요소 처리 기능의 논리 쓸 수
+1

아니요 C++ 14? 그 나쁜. – Yakk

+0

C++ 11이 아니어야합니다. 14 –

+0

왜 C++ 11입니까? 어떤 컴파일러가 지원해야합니까? 많은 사람들이 C++ 14 기능을 일부 갖추고있어 훨씬 쉽게 사용할 수 있습니다. – Yakk

답변

0

: 다음

template<std::size_t I = 0, class ...Ts> 
inline typename std::enable_if<I == sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { } // base case: do nothing 

template<std::size_t I = 0, class... Ts> 
inline constexpr typename std::enable_if<I < sizeof...(Ts), void>::type 
tupel_el_func(std::tuple<Ts...> &t) { 
    do_logic(get<I>(t)); 
    tupel_el_func<I+1>(t); 
} 

을하고 그래서 같이/일반 do_logic 기능을 전문화했다 :

template <class T> 
void do_logic(T element) { 
    // generic logic 
} 

template <> 
void do_logic(char* element) { 
    // char* logic 
} 
+0

이것은 (내 문제를 완전히 해결 한) 나를 위해 일했다. 그래서 idk 왜 downvote –

+0

당신은 기본 케이스없이 재귀가있다. –

+0

질문문에 기본 사례가 없습니까? –

1

나는 재귀를 완전히 피하기 위해 제안합니다.

나쁜 점은 C++ 11을 사용하므로 std::index_sequencestd::make_index_sequence과 같은 C++ 14 기능을 사용할 수 없다는 점입니다.

하지만 쉽게 시뮬레이션 할 수 있습니다. 이제

template <std::size_t ...> 
struct indexSequence 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSequenceHelper 
{ using type = typename indexSequenceHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next> 
struct indexSequenceHelper<0U, Next ... > 
{ using type = indexSequence<Next ... >; }; 

template <std::size_t N> 
using makeIndexSequence = typename indexSequenceHelper<N>::type; 

와 예에 의해, 당신은을 위해 N-1하려면 range에, 0에서 인덱스 (목록을 일반 std::tuple 객체를받을 함수 foo()를 작성하고 튜플을 통과하는 도우미 함수 fooH()를 호출 할 수 있습니다 std::tuple) types` N

template <typename ... Ts> 
void foo (std::tuple<Ts...> const & t) 
{ fooH(t, makeIndexSequence<sizeof...(Ts)>{}); } 

다음으로, 재귀 및 std::get 및 인덱스를 사용하지 않고, 당신은 튜플의 모든 요소를 ​​통해, 실행하는 도우미 함수, 함수를 구현할 수 있습니다 bar()

template <typename ... Ts, std::size_t ... Is> 
void fooH (std::tuple<Ts...> const & t, indexSequence<Is...> const &) 
{ 
    using unused = int[]; 

    (void)unused { 0, ((void)bar(std::get<Is>(t)), 0)... }; 
} 

지금 당신은 당신이 원하는대로, (다음 예에서, long에 대한) 특정 유형, 고유의 템플릿을 bar() 기능

template <typename T> 
void bar (T const & t) 
{ std::cout << "- generic bar(): " << t << std::endl; } 

과 같은 많은 특정 오버로드 된 버전을 개발

void bar (long l) 
{ std::cout << "- bar(), long version: " << l << std::endl; } 

다음은 전체 C++ 11 작업 예제입니다.

#include <tuple> 
#include <iostream> 

template <std::size_t ...> 
struct indexSequence 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSequenceHelper 
{ using type = typename indexSequenceHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next> 
struct indexSequenceHelper<0U, Next ... > 
{ using type = indexSequence<Next ... >; }; 

template <std::size_t N> 
using makeIndexSequence = typename indexSequenceHelper<N>::type; 

template <typename T> 
void bar (T const & t) 
{ std::cout << "- generic bar(): " << t << std::endl; } 

void bar (long l) 
{ std::cout << "- bar(), long version: " << l << std::endl; } 

template <typename ... Ts, std::size_t ... Is> 
void fooH (std::tuple<Ts...> const & t, indexSequence<Is...> const &) 
{ 
    using unused = int[]; 

    (void)unused { 0, ((void)bar(std::get<Is>(t)), 0)... }; 
} 

template <typename ... Ts> 
void foo (std::tuple<Ts...> const & t) 
{ fooH(t, makeIndexSequence<sizeof...(Ts)>{}); } 

int main() 
{ 
    std::tuple<short, int, long, long long> t { 0, 1, 2L, 3LL }; 

    foo(t); 
} 
+1

index_sequence와 make_index_sequence를 정확히 구현하는 것이 더 좋을 것입니다. 따라서 OP a) 사용법을 배우고 b) 14로 전환하면 구현을 그냥 삭제할 수 있습니다. –

+0

@NirFriedman - 글쎄 ... 나는 똑같은 이름을 사용하는 것이 좋은 생각이라고 생각하지 않지만 좋은 지적이다. 평소처럼 내 이름 선택은 끔찍합니다. 더 나은 (나는 희망한다) 이름으로 수정 된 답변. – max66

관련 문제