std :: tuple의 요소를 어떻게 반복 할 수 있습니까?


112

튜플 (C ++ 11 사용)을 어떻게 반복 할 수 있습니까? 다음을 시도했습니다.

for(int i=0; i<std::tuple_size<T...>::value; ++i) 
  std::get<i>(my_tuple).do_sth();

그러나 이것은 작동하지 않습니다.

오류 1 : 죄송합니다. 구현되지 않았습니다. 'Listener ...'를 고정 길이 인수 목록으로 확장 할 수 없습니다.
오류 2 : 상수 표현식에 나타날 수 없습니다.

그렇다면 튜플의 요소를 올바르게 반복하는 방법은 무엇입니까?


2
C ++ 0x에서 어떻게 컴파일하는지 물어봐도 될까요? 내가 아는 한 출시되거나 준비되지 않았습니다.
Burkhard

5
g ++에는 버전 4.3부터 가변 템플릿을 포함한 일부 C ++ 0X 기능에 대한 실험적 지원이 포함되어 있습니다. 다른 컴파일러도 동일한 작업을 수행합니다 (다른 기능 세트를 사용하여 프로덕션에서 사용하려는 경우
최신

std = c ++ 0x와 함께 g ++ 버전 4.4를 사용하고 있습니다

9
이 질문에는 C ++ 11 업데이트가 필요합니다.
Omnifarious 2013

2
@Omnifarious 지금, 그것은 필요 (14) 업데이 트 C ++
pepper_chico

답변:


26

Boost.Fusion 은 가능성이 있습니다.

테스트되지 않은 예 :

struct DoSomething
{
    template<typename T>
    void operator()(T& t) const
    {
        t.do_sth();
    }
};

tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());

@ViktorSehr AFAICT 그렇지 않습니다 (적어도 GCC 4.7.2에서는)? 힌트가있는 분?
sehe apr

@ViktorSehr 문제 발견 : 버그 / 누락으로 인해 퓨전 동작이 포함 순서에 따라 달라집니다. 자세한 내용 은 부스트 티켓 # 8418 을 참조하십시오
sehe

std :: tuple 대신 boost :: fusion :: tuple을 사용해야 작동합니다.
Marcin

GCC 8.1 / mingw-64에서 std 람다 식에 boost :: fusion :: for_each를 사용하면 두 가지 경고가 표시됩니다. boost / mpl / assert.hpp : 188 : 21 : 경고 : 'assert_arg'선언에 불필요한 괄호가 있습니다. [-Wparentheses] 실패 ************ (Pred :: ************ boost / mpl / assert.hpp : 193 : 21 : 경고 : 불필요한 괄호 'assert_not_arg'[-Wparentheses] 선언 실패 ************ (boost :: mpl :: not_ <Pred> :: ************
Hossein

129

튜플 에 대한 반복을 기반으로 한 답변이 있습니다 .

#include <tuple>
#include <utility> 
#include <iostream>

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  { }

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  print(std::tuple<Tp...>& t)
  {
    std::cout << std::get<I>(t) << std::endl;
    print<I + 1, Tp...>(t);
  }

int
main()
{
  typedef std::tuple<int, float, double> T;
  T t = std::make_tuple(2, 3.14159F, 2345.678);

  print(t);
}

일반적인 아이디어는 컴파일 시간 재귀를 사용하는 것입니다. 사실,이 아이디어는 원본 튜플 문서에서 언급 한 것처럼 형식이 안전한 printf를 만드는 데 사용됩니다.

이것은 for_eachfor tuples 로 쉽게 일반화 될 수 있습니다 :

#include <tuple>
#include <utility> 

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
  { }

template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  for_each(std::tuple<Tp...>& t, FuncT f)
  {
    f(std::get<I>(t));
    for_each<I + 1, FuncT, Tp...>(t, f);
  }

그러면 FuncT튜플이 포함 할 수있는 모든 유형에 대해 적절한 오버로드로 무언가를 나타 내기 위해 약간의 노력이 필요 합니다. 이것은 모든 튜플 요소가 공통 기본 클래스 또는 유사한 것을 공유한다는 것을 알고있는 경우 가장 잘 작동합니다.


5
멋진 간단한 예를 들어 주셔서 감사합니다. 작동 방식에 대한 배경 지식을 찾는 C ++ 초보자는 SFINAEenable_if문서를 참조하십시오 .
Faheem Mitha

이것은 쉽게 일반화 될 수 있습니다 for_each. 사실 제가 직접 해봤습니다. :-) 이미 일반화 된 경우이 답변이 더 유용 할 것이라고 생각합니다.
Omnifarious 2013

4
거기에서 저는 실제로 필요했기 때문에 일반화를 추가했으며 다른 사람들이 볼 수 있도록 유용 할 것이라고 생각합니다.
Omnifarious 2013

2
참고 : const std::tuple<Tp...>&.. 버전이 필요할 수도 있습니다 . 반복하는 동안 튜플을 수정하지 않으려면 해당 const버전으로 충분합니다.
lethal-guitar

2
작성된대로가 아닙니다. 인덱싱이 뒤집힌 버전을 만들 수 있습니다. I = sizeof ... (Tp)에서 시작하고 카운트 다운합니다. 그런 다음 최대 인수 수를 명시 적으로 제공하십시오. break_t와 같이 태그 유형을 깨는 버전을 만들 수도 있습니다. 그런 다음 인쇄를 중지하고 싶을 때 해당 태그 유형의 객체를 튜플에 넣습니다. 또는 중지 유형을 템플릿 매개 변수로 제공 할 수 있습니다. 분명히 당신은 런타임에 중단 할 수 없습니다.
emsr 2014

55

C ++ 17에서는 접기 표현식std::apply 과 함께 사용할 수 있습니다 .

std::apply([](auto&&... args) {((/* args.dosomething() */), ...);}, the_tuple);

튜플을 인쇄하는 완전한 예 :

#include <tuple>
#include <iostream>

int main()
{
    std::tuple t{42, 'a', 4.2}; // Another C++17 feature: class template argument deduction
    std::apply([](auto&&... args) {((std::cout << args << '\n'), ...);}, t);
}

[Coliru의 온라인 예]

이 솔루션은 M. Alaggan의 답변 에서 평가 순서 문제를 해결합니다 .


1
여기서 무슨 일이 일어나고 있는지 설명해 주 ((std::cout << args << '\n'), ...);시겠습니까? 람다는 튜플 요소의 압축이 풀린 상태에서 한 번 호출 args되지만 이중 괄호는 어떻게됩니까?
helmesjo

4
@helmesjo ((std::cout << arg1 << '\n'), (std::cout << arg2 << '\n'), (std::cout << arg3 << '\n'))여기 에서 쉼표 식으로 확장됩니다 .
xskxzr

쉼표 표현식에서 합법적이지 않은 작업 (예 : 변수 및 블록 선언)을 수행하려는 경우 모든 것을 메서드에 넣고 접힌 쉼표 표현식 내에서 간단히 호출 할 수 있습니다.
Miral

24

C ++ 17에서는 다음을 수행 할 수 있습니다.

std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);

이것은 std :: experimental :: apply를 사용하여 Clang ++ 3.9에서 이미 작동합니다.


4
do_something()매개 변수 팩이 함수 호출 내에서 확장되어 ()인수의 순서가 지정되지 않았기 때문에 지정되지 않은 순서로 반복, 즉 호출이 발생하지 않습니까? 그것은 매우 중요 할 수 있습니다. 나는 대부분의 사람들이 순서가 회원들과 같은 순서, 즉 std::get<>(). AFAIK, 이와 같은 경우 보장 된 주문을 받으려면 {braces}. 내가 잘못? 이 답변은 이러한 순서에 중점을 둡니다. stackoverflow.com/a/16387374/2757035
underscore_d

21

Boost.Hana 및 일반 람다 사용 :

#include <tuple>
#include <iostream>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/tuple.hpp>

struct Foo1 {
    int foo() const { return 42; }
};

struct Foo2 {
    int bar = 0;
    int foo() { bar = 24; return bar; }
};

int main() {
    using namespace std;
    using boost::hana::for_each;

    Foo1 foo1;
    Foo2 foo2;

    for_each(tie(foo1, foo2), [](auto &foo) {
        cout << foo.foo() << endl;
    });

    cout << "foo2.bar after mutation: " << foo2.bar << endl;
}

http://coliru.stacked-crooked.com/a/27b3691f55caf271


4
제발 가지 마세요 using namespace boost::fusion(특히와 함께 using namespace std). 이제 여부를 알 수있는 방법은 없습니다 for_each입니다 std::for_each또는boost::fusion::for_each
Bulletmagnet

3
@Bulletmagnet 이것은 여기에서 간결하게 수행되었으며 ADL은 문제없이 처리 할 수 ​​있습니다. 게다가, 그것은 또한 로컬 기능입니다.
pepper_chico

16

C ++는 이를 위해 확장 문 을 도입 하고 있습니다. 그들은 원래 C ++ 20을위한 궤도에 있었지만 언어 표현 검토를위한 시간 부족으로 간신히 컷을 놓쳤습니다 ( 여기여기 참조 ).

현재 합의 된 구문 (위 링크 참조)은 다음과 같습니다.

{
    auto tup = std::make_tuple(0, 'a', 3.14);
    template for (auto elem : tup)
        std::cout << elem << std::endl;
}

15

C ++ 17에서이 작업을 수행하는보다 간단하고 직관적이며 컴파일러 친화적 인 방법은 다음과 if constexpr같습니다.

// prints every element of a tuple
template<size_t I = 0, typename... Tp>
void print(std::tuple<Tp...>& t) {
    std::cout << std::get<I>(t) << " ";
    // do things
    if constexpr(I+1 != sizeof...(Tp))
        print<I+1>(t);
}

이것은 @emsr이 제시 한 것과 유사한 컴파일 타임 재귀입니다. 그러나 이것은 SFINAE를 사용하지 않으므로 (제 생각에) 컴파일러 친화적입니다.


8

여기에 Boost.Tuple과 함께 표시된 템플릿 메타 프로그래밍을 사용해야합니다.

#include <boost/tuple/tuple.hpp>
#include <iostream>

template <typename T_Tuple, size_t size>
struct print_tuple_helper {
    static std::ostream & print( std::ostream & s, const T_Tuple & t ) {
        return print_tuple_helper<T_Tuple,size-1>::print( s, t ) << boost::get<size-1>( t );
    }
};

template <typename T_Tuple>
struct print_tuple_helper<T_Tuple,0> {
    static std::ostream & print( std::ostream & s, const T_Tuple & ) {
        return s;
    }
};

template <typename T_Tuple>
std::ostream & print_tuple( std::ostream & s, const T_Tuple & t ) {
    return print_tuple_helper<T_Tuple,boost::tuples::length<T_Tuple>::value>::print( s, t );
}

int main() {

    const boost::tuple<int,char,float,char,double> t( 0, ' ', 2.5f, '\n', 3.1416 );
    print_tuple( std::cout, t );

    return 0;
}

C ++ 0x에서는 print_tuple()대신 가변 템플릿 함수로 작성할 수 있습니다 .


8

먼저 몇 가지 인덱스 도우미를 정의합니다.

template <size_t ...I>
struct index_sequence {};

template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

함수를 사용하여 각 튜플 요소에 적용하고 싶습니다.

template <typename T>
/* ... */ foo(T t) { /* ... */ }

당신은 쓸 수 있습니다:

template<typename ...T, size_t ...I>
/* ... */ do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    std::tie(foo(std::get<I>(ts)) ...);
}

template <typename ...T>
/* ... */ do_foo(std::tuple<T...> &ts) {
    return do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}

또는 foo반환하는 void경우 사용

std::tie((foo(std::get<I>(ts)), 1) ... );

참고 : C ++ 14 make_index_sequence에서는 이미 정의되어 있습니다 ( http://en.cppreference.com/w/cpp/utility/integer_sequence ).

왼쪽에서 오른쪽으로 평가 순서가 필요한 경우 다음과 같이 고려하십시오.

template <typename T, typename ...R>
void do_foo_iter(T t, R ...r) {
    foo(t);
    do_foo(r...);
}

void do_foo_iter() {}

template<typename ...T, size_t ...I>
void do_foo_helper(std::tuple<T...> &ts, index_sequence<I...>) {
    do_foo_iter(std::get<I>(ts) ...);
}

template <typename ...T>
void do_foo(std::tuple<T...> &ts) {
    do_foo_helper(ts, make_index_sequence<sizeof...(T)>());
}

1
가능한 병적 연산자 오버로딩을 피하기 위해 호출 하기 전에 의 반환 값을 foo로 캐스팅해야합니다 . voidoperator,
Yakk-Adam Nevraumont 2014

7

다음은 표준 라이브러리만으로 튜플 항목을 반복하는 쉬운 C ++ 17 방법입니다.

#include <tuple>      // std::tuple
#include <functional> // std::invoke

template <
    size_t Index = 0, // start iteration at 0 index
    typename TTuple,  // the tuple type
    size_t Size =
        std::tuple_size_v<
            std::remove_reference_t<TTuple>>, // tuple size
    typename TCallable, // the callable to bo invoked for each tuple item
    typename... TArgs   // other arguments to be passed to the callable 
>
void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
    if constexpr (Index < Size)
    {
        std::invoke(callable, args..., std::get<Index>(tuple));

        if constexpr (Index + 1 < Size)
            for_each<Index + 1>(
                std::forward<TTuple>(tuple),
                std::forward<TCallable>(callable),
                std::forward<TArgs>(args)...);
    }
}

예:

#include <iostream>

int main()
{
    std::tuple<int, char> items{1, 'a'};
    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
    });
}

산출:

1
a

콜 러블이 값을 반환하는 경우 루프를 조건부로 중단하도록 확장 할 수 있습니다 (그러나 bool 할당 가능 값을 반환하지 않는 콜 러블 (예 : void)에서는 여전히 작동합니다) :

#include <tuple>      // std::tuple
#include <functional> // std::invoke

template <
    size_t Index = 0, // start iteration at 0 index
    typename TTuple,  // the tuple type
    size_t Size =
    std::tuple_size_v<
    std::remove_reference_t<TTuple>>, // tuple size
    typename TCallable, // the callable to bo invoked for each tuple item
    typename... TArgs   // other arguments to be passed to the callable 
    >
    void for_each(TTuple&& tuple, TCallable&& callable, TArgs&&... args)
{
    if constexpr (Index < Size)
    {
        if constexpr (std::is_assignable_v<bool&, std::invoke_result_t<TCallable&&, TArgs&&..., decltype(std::get<Index>(tuple))>>)
        {
            if (!std::invoke(callable, args..., std::get<Index>(tuple)))
                return;
        }
        else
        {
            std::invoke(callable, args..., std::get<Index>(tuple));
        }

        if constexpr (Index + 1 < Size)
            for_each<Index + 1>(
                std::forward<TTuple>(tuple),
                std::forward<TCallable>(callable),
                std::forward<TArgs>(args)...);
    }
}

예:

#include <iostream>

int main()
{
    std::tuple<int, char> items{ 1, 'a' };
    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
    });

    std::cout << "---\n";

    for_each(items, [](const auto& item) {
        std::cout << item << "\n";
        return false;
    });
}

산출:

1
a
---
1

5

std :: tuple을 사용하고 가변 템플릿을 지원하는 C ++ 컴파일러가있는 경우 다음 코드를 사용해보십시오 (g ++ 4.5로 테스트 됨). 이것이 귀하의 질문에 대한 답입니다.

#include <tuple>

// ------------- 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...> 
{}; 

// ----------- FOR EACH -----------------
template<typename Func, typename Last>
void for_each_impl(Func&& f, Last&& last)
{
    f(last);
}

template<typename Func, typename First, typename ... Rest>
void for_each_impl(Func&& f, First&& first, Rest&&...rest) 
{
    f(first);
    for_each_impl( std::forward<Func>(f), rest...);
}

template<typename Func, int ... Indexes, typename ... Args>
void for_each_helper( Func&& f, index_tuple<Indexes...>, std::tuple<Args...>&& tup)
{
    for_each_impl( std::forward<Func>(f), std::forward<Args>(std::get<Indexes>(tup))...);
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

template<typename Func, typename ... Args>
void for_each( std::tuple<Args...>&& tup, Func&& f)
{
   for_each_helper(std::forward<Func>(f), 
                   typename make_indexes<Args...>::type(), 
                   std::forward<std::tuple<Args...>>(tup) );
}

boost :: fusion은 또 다른 옵션이지만 자체 튜플 유형 (boost :: fusion :: tuple)이 필요합니다. 표준을 더 잘 지키자! 다음은 테스트입니다.

#include <iostream>

// ---------- FUNCTOR ----------
struct Functor 
{
    template<typename T>
    void operator()(T& t) const { std::cout << t << std::endl; }
};

int main()
{
    for_each( std::make_tuple(2, 0.6, 'c'), Functor() );
    return 0;
}

가변 템플릿의 힘!


첫 번째 솔루션을 시도했지만 쌍 에서이 기능으로 실패합니다. 이유는 무엇입니까? template <typename T, typename U> void addt (pair <T, U> p) {cout << p.first + p.second << endl; } int main (int argc, char * argv []) {cout << "Hello." << endl; for_each (make_tuple (2,3,4), [] (int i) {cout << i << endl;}); for_each (make_tuple (make_pair (1,2), make_pair (3,4)), addt); 반환 0; }
user2023370

반복하는 방법 (for_each_impl)이 내가 본 모든 솔루션 중에서 가장 우아하다고 생각하기 때문에이 답변이 너무 장황하게 쓰여지는 것은 부끄러운 일입니다.
joki apr

3

MSVC STL에는 _For_each_tuple_element 함수 (문서화되지 않음)가 있습니다.

#include <tuple>

// ...

std::tuple<int, char, float> values{};
std::_For_each_tuple_element(values, [](auto&& value)
{
    // process 'value'
});

2

다른 사람들은 잘 설계된 타사 라이브러리를 언급했습니다. 그러나 타사 라이브러리없이 C ++를 사용하는 경우 다음 코드가 도움이 될 수 있습니다.

namespace detail {

template <class Tuple, std::size_t I, class = void>
struct for_each_in_tuple_helper {
  template <class UnaryFunction>
  static void apply(Tuple&& tp, UnaryFunction& f) {
    f(std::get<I>(std::forward<Tuple>(tp)));
    for_each_in_tuple_helper<Tuple, I + 1u>::apply(std::forward<Tuple>(tp), f);
  }
};

template <class Tuple, std::size_t I>
struct for_each_in_tuple_helper<Tuple, I, typename std::enable_if<
    I == std::tuple_size<typename std::decay<Tuple>::type>::value>::type> {
  template <class UnaryFunction>
  static void apply(Tuple&&, UnaryFunction&) {}
};

}  // namespace detail

template <class Tuple, class UnaryFunction>
UnaryFunction for_each_in_tuple(Tuple&& tp, UnaryFunction f) {
  detail::for_each_in_tuple_helper<Tuple, 0u>
      ::apply(std::forward<Tuple>(tp), f);
  return std::move(f);
}

참고 : 코드는 C ++ 11을 지원하는 모든 컴파일러로 컴파일되며 표준 라이브러리의 디자인과 일관성을 유지합니다.

  1. 튜플은 필요하지 않으며 std::tuple대신 std::get및 지원하는 모든 것이 될 수 있습니다 std::tuple_size. 특히, std::array그리고 std::pair사용될 수 있습니다;

  2. 튜플은 참조 유형이거나 cv-qualified 일 수 있습니다.

  3. 이것은와 유사한 동작을 가지며 std::for_each입력을 반환합니다 UnaryFunction.

  4. C ++ 14 (또는 laster 버전) 사용자의 경우, typename std::enable_if<T>::type그리고 typename std::decay<T>::type자신의 단순화 된 버전으로 대체 될 수 std::enable_if_t<T>std::decay_t<T>;

  5. C ++ 17 (또는 최신 버전) 사용자의 경우 std::tuple_size<T>::value단순화 된 버전 인 std::tuple_size_v<T>.

  6. C ++ 20 (또는 최신 버전) 사용자의 경우이 SFINAE기능은 Concepts.


2

constexprand if constexpr(C ++ 17) 사용 은 매우 간단하고 간단합니다.

template <std::size_t I = 0, typename ... Ts>
void print(std::tuple<Ts...> tup) {
  if constexpr (I == sizeof...(Ts)) {
    return;
  } else {
    std::cout << std::get<I>(tup) << ' ';
    print<I+1>(tup);
  }
}

1

이 기차를 놓쳤을 수도 있지만 나중에 참고할 수 있도록 여기에있을 것입니다.
답변요점을 기반으로 한 내 구성은 다음과 같습니다 .

#include <tuple>
#include <utility>

template<std::size_t N>
struct tuple_functor
{
    template<typename T, typename F>
    static void run(std::size_t i, T&& t, F&& f)
    {
        const std::size_t I = (N - 1);
        switch(i)
        {
        case I:
            std::forward<F>(f)(std::get<I>(std::forward<T>(t)));
            break;

        default:
            tuple_functor<I>::run(i, std::forward<T>(t), std::forward<F>(f));
        }
    }
};

template<>
struct tuple_functor<0>
{
    template<typename T, typename F>
    static void run(std::size_t, T, F){}
};

그런 다음 다음과 같이 사용합니다.

template<typename... T>
void logger(std::string format, T... args) //behaves like C#'s String.Format()
{
    auto tp = std::forward_as_tuple(args...);
    auto fc = [](const auto& t){std::cout << t;};

    /* ... */

    std::size_t some_index = ...
    tuple_functor<sizeof...(T)>::run(some_index, tp, fc);

    /* ... */
}

개선의 여지가있을 수 있습니다.


OP의 코드에 따라 다음과 같이됩니다.

const std::size_t num = sizeof...(T);
auto my_tuple = std::forward_as_tuple(t...);
auto do_sth = [](const auto& elem){/* ... */};
for(int i = 0; i < num; ++i)
    tuple_functor<num>::run(i, my_tuple, do_sth);

1

여기, 여기여기 에서 본 모든 답변 중에서 @sigidagi 의 반복 방식이 가장 좋았 습니다 . 불행히도 그의 대답은 매우 장황하여 내 의견으로는 본질적인 명확성을 모호하게합니다.

이것은 더 간결하고 작동 자신의 솔루션의 내 버전입니다 std::tuple, std::pair하고 std::array.

template<typename UnaryFunction>
void invoke_with_arg(UnaryFunction)
{}

/**
 * Invoke the unary function with each of the arguments in turn.
 */
template<typename UnaryFunction, typename Arg0, typename... Args>
void invoke_with_arg(UnaryFunction f, Arg0&& a0, Args&&... as)
{
    f(std::forward<Arg0>(a0));
    invoke_with_arg(std::move(f), std::forward<Args>(as)...);
}

template<typename Tuple, typename UnaryFunction, std::size_t... Indices>
void for_each_helper(Tuple&& t, UnaryFunction f, std::index_sequence<Indices...>)
{
    using std::get;
    invoke_with_arg(std::move(f), get<Indices>(std::forward<Tuple>(t))...);
}

/**
 * Invoke the unary function for each of the elements of the tuple.
 */
template<typename Tuple, typename UnaryFunction>
void for_each(Tuple&& t, UnaryFunction f)
{
    using size = std::tuple_size<typename std::remove_reference<Tuple>::type>;
    for_each_helper(
        std::forward<Tuple>(t),
        std::move(f),
        std::make_index_sequence<size::value>()
    );
}

데모 : coliru

C ++ 14 는 C ++ 11 용std::make_index_sequence 으로 구현 될 수 있습니다 .


0

부스트의 튜플 도우미 기능을 제공 get_head()하고 get_tail()도우미 기능은 다음과 같이 보일 수 있습니다 :

inline void call_do_sth(const null_type&) {};

template <class H, class T>
inline void call_do_sth(cons<H, T>& x) { x.get_head().do_sth(); call_do_sth(x.get_tail()); }

여기에 설명 된대로 http://www.boost.org/doc/libs/1_34_0/libs/tuple/doc/tuple_advanced_interface.html

std::tuple그것과 유사해야합니다.

실제로 불행히도 std::tuple이러한 인터페이스를 제공하지 않는 것 같으므로 이전에 제안한 방법이 작동하거나 boost::tuple다른 이점 이 있는 것으로 전환해야합니다 (이미 제공된 io 운영자). boost::tuplegcc 에는 단점이 있지만 아직 가변 템플릿을 허용하지 않지만 내 컴퓨터에 최신 버전의 부스트가 설치되어 있지 않으므로 이미 수정되었을 수 있습니다.


0

함수 객체의 튜플을 반복하는 데 동일한 문제가 발생 했으므로 여기에 한 가지 해결책이 더 있습니다.

#include <tuple> 
#include <iostream>

// Function objects
class A 
{
    public: 
        inline void operator()() const { std::cout << "A\n"; };
};

class B 
{
    public: 
        inline void operator()() const { std::cout << "B\n"; };
};

class C 
{
    public:
        inline void operator()() const { std::cout << "C\n"; };
};

class D 
{
    public:
        inline void operator()() const { std::cout << "D\n"; };
};


// Call iterator using recursion.
template<typename Fobjects, int N = 0> 
struct call_functors 
{
    static void apply(Fobjects const& funcs)
    {
        std::get<N>(funcs)(); 

        // Choose either the stopper or descend further,  
        // depending if N + 1 < size of the tuple. 
        using caller = std::conditional_t
        <
            N + 1 < std::tuple_size_v<Fobjects>,
            call_functors<Fobjects, N + 1>, 
            call_functors<Fobjects, -1>
        >;

        caller::apply(funcs); 
    }
};

// Stopper.
template<typename Fobjects> 
struct call_functors<Fobjects, -1>
{
    static void apply(Fobjects const& funcs)
    {
    }
};

// Call dispatch function.
template<typename Fobjects>
void call(Fobjects const& funcs)
{
    call_functors<Fobjects>::apply(funcs);
};


using namespace std; 

int main()
{
    using Tuple = tuple<A,B,C,D>; 

    Tuple functors = {A{}, B{}, C{}, D{}}; 

    call(functors); 

    return 0; 
}

산출:

A 
B 
C 
D

0

또 다른 옵션은 튜플에 대한 반복기를 구현하는 것입니다. 이는 표준 라이브러리 및 범위 기반 for 루프에서 제공하는 다양한 알고리즘을 사용할 수 있다는 장점이 있습니다. 이에 대한 우아한 접근 방식은 https://foonathan.net/2017/03/tuple-iterator/에 설명되어 있습니다 . 기본 아이디어는 반복자를 제공 하는 begin()end()메서드를 사용 하여 튜플을 범위로 바꾸는 것입니다 . 반복기 자체 std::variant<...>는를 사용하여 방문 할 수 있는를 반환합니다 std::visit.

다음은 몇 가지 예입니다.

auto t = std::tuple{ 1, 2.f, 3.0 };
auto r = to_range(t);

for(auto v : r)
{
    std::visit(unwrap([](auto& x)
        {
            x = 1;
        }), v);
}

std::for_each(begin(r), end(r), [](auto v)
    {
        std::visit(unwrap([](auto& x)
            {
                x = 0;
            }), v);
    });

std::accumulate(begin(r), end(r), 0.0, [](auto acc, auto v)
    {
        return acc + std::visit(unwrap([](auto& x)
        {
            return static_cast<double>(x);
        }), v);
    });

std::for_each(begin(r), end(r), [](auto v)
{
    std::visit(unwrap([](const auto& x)
        {
            std::cout << x << std::endl;
        }), v);
});

std::for_each(begin(r), end(r), [](auto v)
{
    std::visit(overload(
        [](int x) { std::cout << "int" << std::endl; },
        [](float x) { std::cout << "float" << std::endl; },
        [](double x) { std::cout << "double" << std::endl; }), v);
});

내 구현 (위 링크의 설명에 크게 기반 함) :

#ifndef TUPLE_RANGE_H
#define TUPLE_RANGE_H

#include <utility>
#include <functional>
#include <variant>
#include <type_traits>

template<typename Accessor>
class tuple_iterator
{
public:
    tuple_iterator(Accessor acc, const int idx)
        : acc_(acc), index_(idx)
    {

    }

    tuple_iterator operator++()
    {
        ++index_;
        return *this;
    }

    template<typename T>
    bool operator ==(tuple_iterator<T> other)
    {
        return index_ == other.index();
    }

    template<typename T>
    bool operator !=(tuple_iterator<T> other)
    {
        return index_ != other.index();
    }

    auto operator*() { return std::invoke(acc_, index_); }

    [[nodiscard]] int index() const { return index_; }

private:
    const Accessor acc_;
    int index_;
};

template<bool IsConst, typename...Ts>
struct tuple_access
{
    using tuple_type = std::tuple<Ts...>;
    using tuple_ref = std::conditional_t<IsConst, const tuple_type&, tuple_type&>;

    template<typename T>
    using element_ref = std::conditional_t<IsConst,
        std::reference_wrapper<const T>,
        std::reference_wrapper<T>>;

    using variant_type = std::variant<element_ref<Ts>...>;
    using function_type = variant_type(*)(tuple_ref);
    using table_type = std::array<function_type, sizeof...(Ts)>;

private:
    template<size_t Index>
    static constexpr function_type create_accessor()
    {
        return { [](tuple_ref t) -> variant_type
        {
            if constexpr (IsConst)
                return std::cref(std::get<Index>(t));
            else
                return std::ref(std::get<Index>(t));
        } };
    }

    template<size_t...Is>
    static constexpr table_type create_table(std::index_sequence<Is...>)
    {
        return { create_accessor<Is>()... };
    }

public:
    static constexpr auto table = create_table(std::make_index_sequence<sizeof...(Ts)>{}); 
};

template<bool IsConst, typename...Ts>
class tuple_range
{
public:
    using tuple_access_type = tuple_access<IsConst, Ts...>;
    using tuple_ref = typename tuple_access_type::tuple_ref;

    static constexpr auto tuple_size = sizeof...(Ts);

    explicit tuple_range(tuple_ref tuple)
        : tuple_(tuple)
    {
    }

    [[nodiscard]] auto begin() const 
    { 
        return tuple_iterator{ create_accessor(), 0 };
    }

    [[nodiscard]] auto end() const 
    { 
        return tuple_iterator{ create_accessor(), tuple_size };
    }

private:
    tuple_ref tuple_;

    auto create_accessor() const
    { 
        return [this](int idx)
        {
            return std::invoke(tuple_access_type::table[idx], tuple_);
        };
    }
};

template<bool IsConst, typename...Ts>
auto begin(const tuple_range<IsConst, Ts...>& r)
{
    return r.begin();
}

template<bool IsConst, typename...Ts>
auto end(const tuple_range<IsConst, Ts...>& r)
{
    return r.end();
}

template <class ... Fs>
struct overload : Fs... {
    explicit overload(Fs&&... fs) : Fs{ fs }... {}
    using Fs::operator()...;

    template<class T>
    auto operator()(std::reference_wrapper<T> ref)
    {
        return (*this)(ref.get());
    }

    template<class T>
    auto operator()(std::reference_wrapper<const T> ref)
    {
        return (*this)(ref.get());
    }
};

template <class F>
struct unwrap : overload<F>
{
    explicit unwrap(F&& f) : overload<F>{ std::forward<F>(f) } {}
    using overload<F>::operator();
};

template<typename...Ts>
auto to_range(std::tuple<Ts...>& t)
{
    return tuple_range<false, Ts...>{t};
}

template<typename...Ts>
auto to_range(const std::tuple<Ts...>& t)
{
    return tuple_range<true, Ts...>{t};
}


#endif

읽기 전용 액세스도를 전달하여 지원 const std::tuple<>&to_range().


0

@Stypox 답변을 확장하면 솔루션을보다 일반적으로 만들 수 있습니다 (C ++ 17 이상). 호출 가능한 함수 인수를 추가하여 :

template<size_t I = 0, typename... Tp, typename F>
void for_each_apply(std::tuple<Tp...>& t, F &&f) {
    f(std::get<I>(t));
    if constexpr(I+1 != sizeof...(Tp)) {
        for_each_apply<I+1>(t, std::forward<F>(f));
    }
}

그런 다음 각 유형을 방문 할 전략이 필요합니다.

몇 가지 도우미 (처음 두 개는 cppreference에서 가져옴)부터 시작하겠습니다.

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
template<class ... Ts> struct variant_ref { using type = std::variant<std::reference_wrapper<Ts>...>; };

variant_ref 튜플의 상태를 수정하는 데 사용됩니다.

용법:

std::tuple<Foo, Bar, Foo> tuples;

for_each_apply(tuples,
               [](variant_ref<Foo, Bar>::type &&v) {
                   std::visit(overloaded {
                       [](Foo &arg) { arg.foo(); },
                       [](Bar const &arg) { arg.bar(); },
                   }, v);
               });

결과:

Foo0
Bar
Foo0
Foo1
Bar
Foo1

완전성을 위해 내 Bar& Foo:

struct Foo {
    void foo() {std::cout << "Foo" << i++ << std::endl;}
    int i = 0;
};
struct Bar {
    void bar() const {std::cout << "Bar" << std::endl;}
};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.