一、std::tuple
std::tuple在应用中有着独特的作用,它本身可以存储非同质化的数据类型,这个在某些场合下非常有用。std::tuple的初级应用,如生成和获取,在前面的几篇文章中已经进行了较详细的说明。但std::tuple仍然有一些复杂的应用,在实际的应用过程中有很大的作用,比如遍历,本文将在这些角度上进行举例分析。
二、复合操作
1、遍历
std::tuple的遍历,在STL库并未提供,它的实现机理是一个特化的过程,所以不能将其理解为容器,这也是没有提供遍历的可能吧。在前面讲过的遍历变参的基础上,其实还是比较容易想到如何遍历tuple的:
template <class Tp, std::size_t N> struct TupleData {static void output(const Tp &t) {TupleData<Tp, N - 1>::output(t);std::cout << std::get<N - 1>(t) << std::endl;}
};
//利用模板的偏特化
template <class Tp> struct TupleData<Tp, 1> {static void output(const Tp &t) { std::cout << std::get<0>(t) << std::endl; }
};template <class... Args> void PrintTuple(const std::tuple<Args...> &t) {TupleData<decltype(t), sizeof...(Args)>::output(t);
}
void ForEachTuple() { PrintTuple(std::make_tuple("id", "123456", 23, "man")); }
而使用make_index_sequence方式遍历Tuple需要C++17以上的版本,这个在前面的文章中提供过,这里再拷贝一次对比:
template <typename Tp, std::size_t... ID> void DisplayTuple(const Tp &tp, std::index_sequence<ID...>) {((std::cout << std::get<ID>(tp) << std::endl), ...);
}template <typename... Args> void GetTuple(const std::tuple<Args...> &tp) {DisplayTuple(tp, std::index_sequence_for<Args...>{});//等同std::make_index_sequence<sizeof...(Args)>{}
}
也可以使用std::apply自动解包:
template <typename T, typename F> auto static useApplyDisplay(T &&tp, F &&fn) {std::apply([&fn](auto &&...args) { ((fn(args)), ...); }, tp);
}int main() {std::tuple tp("abc", 3, 66, '4', "object");auto displayTuple = [](auto &&a) { std::cout << a << " "; };useApplyDisplay(tp, displayTuple);return 0;
}
需要注意的是,无法直接象遍历容器一样使用变量索引std::get之类的方式去遍历模板中的tuple。遍历的方法有很多,开源库和网上有不少的相关例程,特别是随着C++更新的标准出台,更多简单易行的方法会跟随出来。
- 可参看前文“c++17中的apply和make_from_tuple”
2、组合
这个和std::tuple_cat类似:
template <typename Tp1, typename Tp2, std::size_t... Id1, std::size_t... Id2>
constexpr auto tupleCat(const Tp1 &tp1, const Tp2 &tp2, std::index_sequence<Id1...>, std::index_sequence<Id2...>) {return std::make_tuple(std::get<Id1>(tp1)..., std::get<Id2>(tp2)...);
}template <typename... Args1, typename... Args2>
constexpr auto operator+(const std::tuple<Args1...> &t1, const std::tuple<Args2...> &t2) {return tupleCat(t1, t2, std::index_sequence_for<Args1...>{}, std::index_sequence_for<Args2...>{});
}
int main() {std::tuple t1{1, 3.0f, "abc"};std::tuple t2{33, "1b8c", 'a'};auto x = t1 + t2;return 0;
}
其实弄明白了std::index_sequence, 这些都好理解了。还是要抓住基础中的关键环节,这才是灵活应用的前提。
三、总结
std::tuple很多人看上去觉得有些鸡肋,确实也如此,从易用角度来看,它还需要一段路来走。不过,还是那句话,只有最合适的,没有最好的。只要有伯乐,总会有千里马。把它用在合适的场景上,就能够起到事半功倍的效果。