一、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{}
}
也可以使用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++更新的标准出台,更多简单易行的方法会跟随出来。
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很多人看上去觉得有些鸡肋,确实也如此,从易用角度来看,它还需要一段路来走。不过,还是那句话,只有最合适的,没有最好的。只要有伯乐,总会有千里马。把它用在合适的场景上,就能够起到事半功倍的效果。