C++11与C++14遍历tuple

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yockie/article/details/89511498

概述

tuple,元组类型。头文件,tuple是一个固定大小的不同类型(异质,heterogeneous)值的集合(这一点是tuple与其他常规STL容器的最大不同,即它可以同时存放不同类型的数据),是泛化的std::pair(也即std::pair是tuple的一个特例,长度受限为2)。

错误方法

常规思维对tuple的遍历输出

template<typename... Args>
std::ostream& operator<<(std::ostream& os, const std::tuple<Args...>& t)
{
    os << "(" << std::get<0>(t);
    for (size_t i = 1; i < sizeof...(Args) << ++i)
        os << ", " << std::get<i>(t);
    return os << "]";
}

int main(int, char**)
{
    cout << make_tuple("InsideZhang", 23, "HeNan") << endl;
            // 编译出错,局部变量i不可作为非类型模板参数
    return 0;
}

关于非类型模板参数,可以参考这篇文章:https://blog.csdn.net/lanchunhui/article/details/49634077

这时我们便需要单独把每一位的索引拿出来作为非类型模板参数进行递归调用:

template<typename Tuple>
void tuple_print(const Tuple& t, size_t N, std::ostream& os)
{
    if (N != 1)
        tuple_print(t, N-1, os);
    os << std::get<N-1>(t);
}

以上的代码, 也即通过参数传递的方式进行的非类型模板参数的赋值,仍然编译不通过。非类型模板参数必须在编译时即为常量表达式。

C++11方法:

template<typename Tuple, size_t N>
struct tuple_print
{
    static void print(const Tuple& t, std::ostream& os)
    {
        tuple_print<Tuple, N-1>::print(t, os);
        os << ", " << std::get<N-1>(t); 
    }
};
// 类模板的特化版本
template<typename Tuple>
struct tuple_print<Tuple, 1>
{
    static void print(const Tuple& t, std::ostream& os)
    {
        os << "(" << std::get<0>(t); 
    }
};

// operator<<
template<typename... Args>
std::ostream& operaotr<<(std::ostream& os, const std::tuple<Args...>& t)
{
    tuple_print<decltype(t), sizeof...(Args)>::print(t, os);
    return os << ")";
}

客户端代码:

auto t1 = std::make_tuple("InsideZhang", 23, "HeNan");
auto t2 = std::make_tuple("InsideLi", 23, "AnHui");
// tuple_cat()   元组拼接
// 调用operator<< 运算符重载
std::cout << std::tuple_cat(t1, t2) << std::endl;
//  (InsideZhang, 23, HeNan, InsideLi, 23, AnHui)

因为tuple类没有重载operator[]运算符,很难像其他容器和数据额结构一样进行简洁的索引动作,只能通过一个全局的std::get<size_t>(t)进行操作。

C++14方法:

通过std::integer_sequence或其辅助函数,可以快速实现

template< class T, T... Ints >
struct integer_sequence;

类模板 std::integer_sequence 表示一个编译时的整数序列。在用作函数模板的实参时,能推导参数包 Ints 并将它用于包展开。
成员函数:
size 返回 Ints 中的元素数 (公开静态成员函数),等价于 sizeof…(Ints)

为帮助 T 为 std::size_t 的常用情况,定义别名模板 std::index_sequence 。

template<std::size_t... Ints>
using index_sequence = std::integer_sequence<std::size_t, Ints...>;
定义辅助模板 std::make_integer_sequence 以简化以 0, 1, 2, ..., N-1 为 Ints 创建 std::integer_sequence 与 std::index_sequence :

template<class T, T N>
using make_integer_sequence = std::integer_sequence<T, /* a sequence 0, 1, 2, ..., N-1 */ >;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
若 N 为负则程序为病式。若 N 为零,则指示类型为 integer_sequence<T> 。

定义辅助类模板 std::index_sequence_for ,以转换任何类型参数包为同长度的下标序列。

template<class... T>
using index_sequence_for = std::make_index_sequence<sizeof...(T)>;

实现:

#include <tuple>
#include <iostream>
#include <array>
#include <utility>
 
// 转换数组为 tuple
template<typename Array, std::size_t... I>
auto a2t_impl(const Array& a, std::index_sequence<I...>)
{
    return std::make_tuple(a[I]...);
}
 
template<typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
auto a2t(const std::array<T, N>& a)
{
    return a2t_impl(a, Indices{});
}
 
// 漂亮地打印 tuple
 
template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(std::basic_ostream<Ch,Tr>& os,
                      const Tuple& t,
                      std::index_sequence<Is...>)
{
    ((os << (Is == 0? "" : ", ") << std::get<Is>(t)), ...);
}
 
template<class Ch, class Tr, class... Args>
auto& operator<<(std::basic_ostream<Ch, Tr>& os, const std::tuple<Args...>& t)
{
    os << "(";
    print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
    return os << ")";
}

客户端函数:

int main()
{
    std::array<int, 4> array = {1,2,3,4};
 
    // 转换 array 为 tuple
    auto tuple = a2t(array);
    static_assert(std::is_same<decltype(tuple),
                               std::tuple<int, int, int, int>>::value, "");
 
    // 打印到 cout
    std::cout << tuple << '\n';
    //输出:(1, 2, 3, 4)
}

参考:
1.https://blog.csdn.net/lanchunhui/article/details/49868077
2.https://zh.cppreference.com/w/cpp/utility/integer_sequence

猜你喜欢

转载自blog.csdn.net/yockie/article/details/89511498