標籤:

如何遍歷 std::tuple?

希望以 C++11 實現,不要 C++14 或更新的標準


正好最近寫過一些模板,試著來答一下。

---------------- 無視部分開始的分割線 ----------------

(下面這段寫的像*一樣還是別看了……寫模板寫傻了的產物)

首先,實現一個 C++14 才有的 std::make_index_sequence (C++11可用,參考Implementation C++14 make_integer_sequence):

template &
struct Base {};

template &
struct Concat_;
template &
using Concat = typename Concat_&::Type;
template &
struct Concat_&, Base&&> {

using Type = Base&;

};

template &
struct Index_;
template &
using Index = typename Index_&::Type;
template &
struct Index_&::type&> {

using Type = Base&;

};
template &
struct Index_&::type&> {

using Type = Concat&, Index&<(B + E) / 2 + 1, E&>&>;

};

然後來實現一下遍歷器(參考Prameter pack):

template &
struct A_ {};
template &::value - 1&>&>
using A = A_&;
template &
struct A_&&> {

template &
static void a(const Tup tup, F f) {

int dummy[] = { (f(std::get&(tup)), 0)... };
(void)dummy;

}

};

最後來遍歷一下:

struct C {

template &
void operator ()(T t) { std::cout &<&< t &<&< std::endl; } }; int main() { auto tup = std::make_tuple(42, 0.5, "F**k the template"); A&::a(tup, C());
return 0;

}

輸出:

42
0.5
F**k the template

---------------- 無視部分結束的分割線 ----------------

似乎寫複雜了,也許正確的做法是直接遞歸模板:

template &
struct B;
template &
struct B&::value == 0&>::type&> {

template &
static void b(const Tup tup, F f) {}

};
template &
struct B&::value - 1)&>::type&> {

template &
static void b(const Tup tup, F f) { f(std::get&(tup)); B&::b(tup, std::forward&(f)); }

};
template &
struct B&::value - 1&>::type&> {

template &
static void b(const Tup tup, F f) { f(std::get&(tup)); }

};

然後

struct C {

template &
void operator ()(T t) { std::cout &<&< t &<&< std::endl; } }; int main() { auto tup = std::make_tuple(42, 0.5, "F**k the template"); B&::b(tup, C());
return 0;

}

結果一樣(逃

(目測會掉粉


自己來答一下吧。

最後我順著 @Irons Du 和 @悲鳴 給的模板偏特化思路,寫了個通用版本:

// tuple_iterator.h

#include &

template&
struct TupleIterator final {
TupleIterator() = delete;
template&
inline static void iterate(
const TupleType t,
void f(const typename std::tuple_element&::type , ArgTypes...),
ArgTypes... args)
{
TupleIterator&::iterate(t, f, args...);
f(std::get&(t), args...);
}
};

template&
struct TupleIterator& final {
TupleIterator() = delete;
template&
static void iterate(
const TupleType ,
void (const typename std::tuple_element&<0, TupleType&>::type , ArgTypes...),
ArgTypes...)
{
}
};

// 這個是為了支持空的 tuple&<&>() 的情況
template&
void TupleForEach(const std::tuple&<&> , void (ArgTypes...), ArgTypes...)
{
}

template&
void TupleForEach(const TupleType t,
void f(const typename std::tuple_element&::value - 1,
TupleType&>::type ,
ArgTypes...),
ArgTypes... args)
{
TupleIterator&::value&>::iterate(t, f, args...);
}

用的時候大概是這樣:

#include &
#include &
#include "tuple_iterator.h"

using std::ostream;
using std::cout;
using std::endl;
using std::make_tuple;

// 執行的函數必須是這樣的形式,第一個參數是 tuple 元素的類型,返回值是 void
template&
void PrintTupleElement(const T element, ostream *os)
{
*os &<&< element &<&< ", "; } // 如果要處理空 tuple&<&>(),則寫一個不含有第一個參數,其他參數類型相同的重載函數,內容可以是空
// 不知道對於 tuple&<&>() 有什麼更好的解決方法
void PrintTupleElement(ostream *os)
{
}

int main()
{
auto t = make_tuple(1, 2.3, "a");
cout &<&< "("; TupleForEach(t, PrintTupleElement, cout); cout &<&< ")" &<&< endl; return 0; }

以上。


template&
struct TupleReader
{
static void read(const rapidjson::Value msg, Tuple t)
{
TupleReader&::read(msg, t);
if (msg.IsObject())
{
rapidjson::Value::ConstMemberIterator itr = msg.FindMember(std::to_string(N - 1).c_str());
readJson((*itr).value, std::get&(t));
}
}
};

template&
struct TupleReader &< Tuple, 1 &>
{
static void read(const rapidjson::Value msg, Tuple t)
{
if (msg.IsObject())
{
rapidjson::Value::ConstMemberIterator itr = msg.FindMember("0");
readJson((*itr).value, std::get&<0&>(t));
}
}
};

https://github.com/IronsDu/dodo/blob/master/src/rpc/JsonRpc.h#L133


去看get的源碼

----

遍歷的話好像去看tie更直接一點...


用lambda遍歷吧:

template&
void tuple_for_each(const std::tuple& t, Func f)
{
__tuple_processor&::__tuple_process(t, f);
}

template&
struct __tuple_processor
{
inline static void __tuple_process(const Tuple t, Func f)
{
__tuple_processor&::__tuple_process(t, f);
f(std::get&(t));
}
};

template&
struct __tuple_processor&
{
inline static void __tuple_process(const Tuple t, Func f)
{
f(std::get&<0&>(t));
}
};

測試:

int main()
{
auto t1 = std::make_tuple(1, "2", 3.4f);
int n = 5;
auto t2 = std::tuple_cat(t1, std::tie(n), std::make_pair(6, 7));
tuple_for_each(t2, [](auto val) { std::cout &<&< val &<&< " "; }); }

輸出:

1 2 3.4 5 6 7


//遍歷輸出tuple元素的簡潔方式(C++11)

//Win32Con17_VS2017_01.cpp

#include &

#include &

using namespace std;

template&

void myprint_impl(tuple& tup) //泛化版本

{

cout &<&< tup._Myfirst._Val &<&< ((tup._Mysize &> 1) ? ", " : ""); //輸出tup的頭1項

myprint_impl(tup._Get_rest()); //對除頭1項之外的tup遞歸調用

}

template&<&>

void myprint_impl(tuple&<&> tup) //終止條件

{

cout &<&< endl;

}

int main()

{

auto t = make_tuple(3, 4.67, "Hello", true);

myprint_impl(t); //3, 4.67, Hello, 1

system("pause");

return 0;

}


#include &
#include &

using namespace std;

template&
struct print_tuple {
void operator() (tuple& t) {
cout &<&< get&(t) &<&< " "; print_tuple&()(t);
}
};

template&
struct print_tuple&<0, Ts...&> {
void operator() (tuple& t) {
cout &<&< get&<0&>(t) &<&< endl; } }; template&
void print(tuple& t) {
const auto size = tuple_size&&>::value;
print_tuple&()(t);
}

int main(void) {
auto t = make_tuple(1, 2, "abc", "def", 4.0f);
print(t);

return 0;
}

// from Iterating over a std::tuple


你是想要用std::apply展開std::tuple嗎?


推薦閱讀:

目前是否有C++提案是關於在編譯期獲取類/結構體成員變數的?
如今C++在非底層環境下還有多少地位?
為什麼非指針對象不能使用const成員函數?
迭代器尾後元素的設計是出於什麼意圖?

TAG:C | C11 |