「这是我参与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 第几个元素出来。
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 的元素个数
tuple t;
int size = std::tuple_size<decltype(t))>::value;
遍历 tuple 中的每个元素
因为 tuple 的参数是变长的,而且类型也可能不同,并没有 for_each 函数,如果我们想遍历 tuple 中的每个元素,需要自己写代码实现。
举例,要打印 tuple 中的每个元素的话,代码如下
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 元素值获取其对应的索引
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 元素进行单独处理
#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;
}