C++11 / C++14 中 tuple 的高级使用 前置

「这是我参与11月更文挑战的第 10 天,活动详情查看:2021最后一次更文挑战」。

参加该活动的第 23 篇文章

前置知识

tuple 和 vector 比较

vector 只能存储同一种类型的数据,tuple 可以存储任意类型的数据;

vector 和 variant 比较

二者都可以存储不同类型的数据,但是 variant 的类型个数是固定的,而 tuple 的类型个数不是固定的,是变长的,更为强大

std::decay

类模板说明

为类型 T 应用从左值到右值(lvalue-to-rvalue)数组到指针(array-to-pointer)函数到指针(function-to-pointer) 的隐式转换。

转换将移除类型 T 的 cv 限定符(const 和 volatile 限定符),并定义结果类型为成员 decay<T>::type 的类型。这种转换很类似于当函数的所有参数按值传递时发生转换。

  • 如果类型 T 是一个函数类型,那么从函数到指针的类型转换将被应用,并且 T 的衰变类型等同于: add_pointer<T>::type
  • 如果类型 T 是一个数组类型,那么从数组到指针的类型转换将被应用,并且 T 的衰变类型等同于:
    add_pointer<remove_extent<remove_reference<T>::type>::type>::type
  • 当左值到右值转换被应用时,T 的衰变类型等同于:remove_cv<remove_reference<T>::type>::type

模板参数说明

T 表示某种类型。

  • 当 T 是引用类型decay<T>::type 返回 T 引用的元素类型;
  • 当 T 是非引用类型decay<T>::type 返回 T 的类型

正文

前文介绍过 C++11 / C++14 中 tuple 的基本使用,接下来将介绍 tuple 的一些高级用法

获取 tuple 中某个位置元素的类型

其中我们通过 std::tuple_element 来获取 tuple 元素的类型。
然后第几个元素就从 tuple std::get 第几个元素出来。

1
2
3
4
5
6
cpp复制代码template <typename Tuple>
void Fun(Tuple &tp)
{
std::tuple_element<0, Tuple>::type first = std::get<0>(mytuple);
std::tuple_element<1, Tuple>::type second = std::get<1>(mytuple);
}

获取 tuple 的元素个数

1
2
cpp复制代码tuple t;
int size = std::tuple_size<decltype(t))>::value;

遍历 tuple 中的每个元素

因为 tuple 的参数是变长的,而且类型也可能不同,并没有 for_each 函数,如果我们想遍历 tuple 中的每个元素,需要自己写代码实现。

举例,要打印 tuple 中的每个元素的话,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cpp复制代码template <class Tuple, std::size_t N>
struct TuplePrinter
{
static void print(const Tuple &t)
{
TuplePrinter<Tuple, N - 1>::print(t); ///< 递归
std::cout << ", " << std::get<N - 1>(t); ///< 第 N-1 个元素
}
};

template <class Tuple>
struct TuplePrinter<Tuple, 1> ///< 递归终止, N == 1
{
static void print(const Tuple &t)
{
std::cout << std::get<0>(t); ///< 第 0 个元素
}
};

/// @note 可变参模板函数
template <class... Args>
void PrintTuple(const std::tuple<Args...> &t)
{
std::cout << "(";
/// @note tuple 类型、参数个数
TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
std::cout << ")\n";
}

根据 tuple 元素值获取其对应的索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
cpp复制代码namespace detail ///< 命名空间
{
template <int I, typename T, typename... Args>
struct find_index ///< 泛化版本
{
static int call(std::tuple<Args...> const &t, T &&val)
{
/// @note 如果找到该值,则返回对应索引,否则继续递归查找
return (std::get<I - 1>(t) == val) ? I - 1 : find_index<I - 1, T, Args...>::call(t, std::forward<T>(val));
}
};

template <typename T, typename... Args>
struct find_index<0, T, Args...> ///< 偏特化,递归终止
{
static int call(std::tuple<Args...> const &t, T &&val)
{
/// @note 如果找到该值,则返回第 0 个索引,否则返回 -1
return (std::get<0>(t) == val) ? 0 : -1;
}
};
}

/// @note 可变参模板函数
template <typename T, typename... Args>
int find_index(std::tuple<Args...> const &t, T &&val)
{
return detail::find_index<sizeof...(Args) - 1, T, Args...>::
call(t, std::forward<T>(val));
}

int main()
{
std::tuple<int, int, int, int> a(2, 3, 1, 4);
std::cout << find_index(a, 1) << std::endl; // Prints 2
std::cout << find_index(a, 2) << std::endl; // Prints 0
std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}

展开 tuple,并将其元素作为函数的参数

这样就可以用统一的形式来调用任意的函数及其参数,我们还能根据需要对 tuple 元素进行单独处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
cpp复制代码#include <tuple>
#include <type_traits>
#include <utility>

template <size_t N>
struct Apply ///< 泛化
{
template <typename F, typename T, typename... A>
static inline auto apply(F &&f, T &&t, A &&...a)
-> decltype(Apply<N - 1>::apply( ///< 推断返回值类型
std::forward<F>(f),
std::forward<T>(t),
std::get<N - 1>(std::forward<T>(t)),
std::forward<A>(a)...))
{
/// @note 递归
return Apply<N - 1>::apply(std::forward<F>(f),
std::forward<T>(t),
std::get<N - 1>(std::forward<T>(t)), ///< 取出并传递相应索引的 tuple 元素
std::forward<A>(a)...);
}
};

template <>
struct Apply<0> ///< 特化,终止递归
{
template <typename F, typename T, typename... A>
static inline auto apply(F &&f, T &&, A &&...a)
-> decltype(std::forward<F>(f)(std::forward<A>(a)...)) ///< 推断返回值类型
{
return std::forward<F>(f)(std::forward<A>(a)...);
}
};

template <typename F, typename T>
inline auto apply(F &&f, T &&t)
-> decltype(Apply<std::tuple_size< ///< 推断返回值类型
typename std::decay<T>::type>::value>::apply(std::forward<F>(f),
std::forward<T>(t)))
{
return Apply<std::tuple_size<
typename std::decay<T>::type>::value>::apply(std::forward<F>(f),
std::forward<T>(t));
}

/// @note 测试函数
void one(int i, double d)
{
std::cout << "function one(" << i << ", " << d << ");\n";
}
int two(int i)
{
std::cout << "function two(" << i << ");\n";
return i;
}

/// @note 测试代码
int main()
{
std::tuple<int, double> tup(23, 4.5);
apply(one, tup);

int d = apply(two, std::make_tuple(2));

return 0;
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%