튜플 (C ++ 11 사용)을 어떻게 반복 할 수 있습니까? 다음을 시도했습니다.
for(int i=0; i<std::tuple_size<T...>::value; ++i)
std::get<i>(my_tuple).do_sth();
그러나 이것은 작동하지 않습니다.
오류 1 : 죄송합니다. 구현되지 않았습니다. ‘Listener …’를 고정 길이 인수 목록으로 확장 할 수 없습니다.
오류 2 : 상수 표현식에 나타날 수 없습니다.
그렇다면 튜플의 요소를 올바르게 반복하는 방법은 무엇입니까?
답변
Boost.Fusion 은 가능성이 있습니다.
테스트되지 않은 예 :
struct DoSomething
{
template<typename T>
void operator()(T& t) const
{
t.do_sth();
}
};
tuple<....> t = ...;
boost::fusion::for_each(t, DoSomething());
답변
#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_each
for 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
튜플이 포함 할 수있는 모든 유형에 대해 적절한 오버로드로 무언가를 나타 내기 위해 약간의 노력이 필요 합니다. 이것은 모든 튜플 요소가 공통 기본 클래스 또는 유사한 것을 공유한다는 것을 알고있는 경우 가장 잘 작동합니다.
답변
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);
}
이 솔루션은 M. Alaggan의 답변 에서 평가 순서 문제를 해결합니다 .
답변
C ++ 17에서는 다음을 수행 할 수 있습니다.
std::apply([](auto ...x){std::make_tuple(x.do_something()...);} , the_tuple);
이것은 std :: experimental :: apply를 사용하여 Clang ++ 3.9에서 이미 작동합니다.
답변
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;
}
답변
C ++는 이를 위해 확장 문 을 도입 하고 있습니다. 그들은 원래 C ++ 20을위한 궤도에 있었지만 언어 표현 검토를위한 시간 부족으로 간신히 컷을 놓쳤습니다 ( 여기 및 여기 참조 ).
현재 합의 된 구문 (위 링크 참조)은 다음과 같습니다.
{
auto tup = std::make_tuple(0, 'a', 3.14);
template for (auto elem : tup)
std::cout << elem << std::endl;
}
답변
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를 사용하지 않으므로 (제 생각에) 컴파일러 친화적입니다.