STL学习笔记(六)——键值对(pair)和元组(tuple)

通用工具——键值对(pair)和元组(tuple)

C++98标准提供了一个简单的pair类,用来处理类型可以不同的两个值。TR1引入了tuple类,用于接受有限多个元素。C++11对pair类进行了扩展,并重新实现了tuple类(采用 可变参数模板技术使其可接受无限多个元素)。

1.键值对(pair)

pair是一个使用模板技术实现的struct结构,定义于<utility>,它将两个类型相同或不同的值对视为一个单元。STL容器map、multimap、unordered_map、unordered_multimap就是使用pair来管理其键值对元素。pair的简单实现如下所示(不熟悉的关键字请参照“ C++11新特性”):
namespace std
{
	// pair的STL实现.
	template <typename _Ty1, typename _Ty2>
	struct pair
	{
		// pair内的类型定义.
		typedef pair<_Ty1, _Ty2> _Myt;
		typedef _Ty1 first_type;
		typedef _Ty2 second_type;

		// pair的键值对.
		_Ty1 first;
		_Ty2 second;

		// pair的模板函数.
		template<typename _Uty1 = _Ty1, typename _Uty2 = _Ty2,
			typename = enable_if_t<is_default_constructible<_Uty1>::value
			&& is_default_constructible<_Uty2>::value>>
			constexpr pair()
			: first(), second()
		{
			// 默认构造函数.
		}

		template<typename _Uty1 = _Ty1, typename _Uty2 = _Ty2>
		constexpr explicit pair(const _Ty1& _Val1, const _Ty2& _Val2)
			: first(_Val1), second(_Val2)
		{
			// 传值的构造函数.
		}

		_Myt& operator=(const _Myt& _Right) // 重载=运算符.
		{
			first = _Right.first;
			second = _Right.second;
			return (*this);
		}

		// Others...
	};
}
接下来我们以代码的形式来了解pair的各种使用方法:
#include <map>      // 包含std::map定义.
#include <string>   // 包含std::string定义.
#include <utility>  // 包含std::pair定义.

/// pair各种应用.
std::string strval = "pair";

// pair的构造函数用法:
std::pair<std::string, double> pa0;                         // 默认构造.
std::pair<std::string, double> pa1(strval, 6.6);            // 拷贝内存构造.
std::pair<std::string, double> pa2(pa1);                    // 拷贝内存构造.
std::pair<std::string, double> pa3 = pa1;                   // 拷贝内存赋值.
std::pair<std::string, double> pa11(std::move(strval), 6.6);// 移动内存构造(详见“搬迁语义move”).
std::pair<std::string, double> pa21(std::move(pa11));       // 移动内存构造.
std::pair<std::string, double> pa31 = std::move(pa21);      // 移动内存赋值.
std::cout << "00 string: " << strval << std::endl;          // 00 string:(内存被搬迁)
std::cout << "01 string: " << pa1.first << std::endl;       // 01 string : pair
std::cout << "02 string: " << pa2.first << std::endl;       // 02 string : pair
std::cout << "03 string: " << pa3.first << std::endl;       // 03 string : pair
std::cout << "11 string: " << pa11.first << std::endl;      // 11 string :(内存被搬迁)
std::cout << "21 string: " << pa21.first << std::endl;      // 21 string :(内存被搬迁)
std::cout << "31 string: " << pa31.first << std::endl;      // 31 string : pair

// pair的辅助函数:
std::pair<int, double> pa_0 = std::make_pair(10, 1.1);      // 辅助函数make_pair().
std::pair<int, double> pa_1 = std::make_pair(20, 2.2);      // 辅助函数make_pair().
int iPairFirst = std::get<0>(pa_0);                         // 辅助函数get<>(),等价于pair.first.
double dblPairSecond = std::get<1>(pa_0);                   // 辅助函数get<>(),等价于pair.second.
std::swap(pa_0, pa_1);                                      // 辅助函数swap,交换两实参对象的值.
std::pair<int, double> pa_01 = std::ref(pa_0);              // 辅助函数ref,取某个对象的索引.

// pair的重载运算符:
std::pair<int, double> pa_3 = pa_0;                         // 重载=运算符.
bool bRet = pa_0 == pa_3;                                   // 重载==运算符.
bool bRet0 = pa_0 != pa_3;                                  // 重载!=运算符.
bool bRet1 = pa_0 < pa_3;                                   // 重载<运算符[cmp(first)&&cmp(second)].
bool bRet2 = pa_0 > pa_3;                                   // 重载>运算符.
bool bRet3 = pa_0 <= pa_3;                                  // 重载<=运算符.
bool bRet4 = pa_0 >= pa_3;                                  // 重载>=运算符.

// pair的其他成员函数:
pa_0.swap(pa_1);                                            // 同std::swap(),交换pa_0和pa_1的数据.

// pair的成员变量可直接读写访问:
// 赋值方式一:动态.
pa0.first = "pair0";
pa0.second = 100.01;
// 赋值方式二:静态
std::get<0>(pa0) = "pair_static";
std::get<1>(pa0) = 99.9;
// 取值方式一:动态.
std::cout << pa0.first << std::endl;
std::cout << pa0.second << std::endl;
// 取值方式二:静态.
std::cout << std::get<0>(pa0) << std::endl;
std::cout << std::get<1>(pa0) << std::endl;

// pair在map中的应用:
typedef std::map<int, int> Map_Int;
typedef std::map<int, int>::iterator Map_Int_Iter;
typedef std::pair<int, int> Map_Int_Pair;
Map_Int map1;
map1.insert(Map_Int_Pair(0, 100));                          // 插入键值对成功.
map1.insert(Map_Int_Pair(1, 101));                          // 插入键值对成功.
map1.insert(Map_Int_Pair(0, 102));                          // 键值冲突,插入失败.
Map_Int_Iter it = map1.begin();
Map_Int_Pair pair1 = *it;                                   // ok.
//Map_Int_Pair& pair = *it;                                 // wrong.
const Map_Int_Pair& pair2 = *it;                            // ok.

2.元组(tuple)

tuple是一个模板类,定义于<tuple>,它采用变长模板技术,将任意多个类型相同或不同的值视为一个单元。pair可看作是一种特殊的tuple,因此pair和tuple也可以相互转化。tuple的简单实现如下所示:
namespace std
{
	template<class... _Types>
	class tuple;

	template<>
	class tuple<> // 空元组定义,用于递归元组的最后一层.
	{
	public:
		typedef tuple<> _Myt;

		constexpr tuple() noexcept
		{	// default construct
		}

		// others...
	};

	template<typename _This, typename... _Rest>
	class tuple<_This, _Rest...> : private tuple<_Rest...> // 递归元组定义.
	{
	public:
		typedef _This _This_type;
		typedef tuple<_This, _Rest...> _Myt;
		typedef tuple<_Rest...> _Mybase;
		static constexpr size_t _Mysize = 1 + sizeof...(_Rest);

		template<typename _Tag, typename _This2, typename... _Rest2>
		constexpr tuple(_Tag, _This2&& _This_arg, _Rest2&&... _Rest_arg)
			: _Mybase(_Exact_args_t{}, _STD forward<_Rest2>(_Rest_arg)...),
			_Myfirst(_STD forward<_This2>(_This_arg))
		{	// construct from one arg per element
		}
	};
}
接下来我们以代码的形式来了解tuple的各种使用方法:
#include <string>   // 包含std::string定义.
#include <utility>  // 包含std::pair定义.
#include <tuple>    // 包含std::tuple定义.

/// tuple各种应用.
std::string strval2 = "tuple";

// tuple的构造函数用法:
std::tuple<std::string, short, double> tp0;                               // 默认构造.
std::tuple<std::string, short, double> tp1(strval2, 20, 30.0);            // 拷贝内存构造.
std::tuple<std::string, short, double> tp2(tp1);                          // 拷贝内存构造.
std::tuple<std::string, short, double> tp3 = tp1;                         // 拷贝内存赋值.
std::tuple<std::string, short, double> tp11(std::move(strval2), 20, 30.0);// 移动内存构造(详见“搬迁语义”).
std::tuple<std::string, short, double> tp21(std::move(tp11));             // 移动内存构造.
std::tuple<std::string, short, double> tp31 = std::move(tp21);            // 移动内存赋值.
std::cout << "00 string: " << strval2 << std::endl;                       // 00 string:(内存被搬迁)
std::cout << "01 string: " << std::get<0>(tp1) << std::endl;              // 01 string : tuple
std::cout << "02 string: " << std::get<0>(tp2) << std::endl;              // 02 string : tuple
std::cout << "03 string: " << std::get<0>(tp3) << std::endl;              // 03 string : tuple
std::cout << "11 string: " << std::get<0>(tp11) << std::endl;             // 11 string :(内存被搬迁)
std::cout << "21 string: " << std::get<0>(tp21) << std::endl;             // 21 string :(内存被搬迁)
std::cout << "31 string: " << std::get<0>(tp31) << std::endl;             // 31 string : tuple

// tuple的辅助函数:
std::tuple<int, double> tp_0 = std::make_tuple(10, 1.1);                  // 辅助函数make_tuple().
std::tuple<int, double> tp_1 = std::make_tuple(20, 2.2);                  // 辅助函数make_tuple().
int iTupleFirst = std::get<0>(tp_0);                                      // 辅助函数get<>(),取第i个元素.
double dblTupleSecond = std::get<1>(tp_0);                                // 辅助函数get<>(),取第i个元素.
std::swap(tp_0, tp_1);                                                    // 辅助函数swap,交换两实参对象的值.
std::tuple<int, double> tp_01 = std::ref(tp_0);                           // 辅助函数ref,取某个对象的索引.
int a; double b; std::tie(a, b) = tp_01;                                  // 辅助函数tie,构造tuple各成员的索引结构.
int nTupleCount = std::tuple_size<std::tuple<int, int, float>>::value;    // 辅助函数tuple_size<>::value,取得tuple的元素个数.
std::tuple_element<0, std::tuple<int, int, float>>::type aInt = 10;       // 辅助函数tuple_element::type,取得tuple第i个元素的类型.
auto tt = std::tuple_cat(std::make_tuple(11, 22.0f, "hello"), std::tie(a));// 辅助函数tuple_cat,连接成一个tuple.

// tuple的重载运算符:
std::tuple<int, double> tp_3 = tp_0;                                      // 重载=运算符.
bool bRet0t = tp_0 == tp_3;                                               // 重载==运算符.
bool bRet00 = tp_0 != tp_3;                                               // 重载!=运算符.
bool bRet01 = tp_0 < tp_3;                                                // 重载<运算符[字典式比较].
bool bRet02 = tp_0 > tp_3;                                                // 重载>运算符.
bool bRet03 = tp_0 <= tp_3;                                               // 重载<=运算符.
bool bRet04 = tp_0 >= tp_3;                                               // 重载>=运算符.

// tuple的其他成员函数:
tp_0.swap(tp_1);                                                          // 同std::swap(),交换tp_0和tp_1的数据.

/// tuple的成员变量的读写访问:
std::string strval0;
short       svalue0;
double      dblval0;
// 取值方式一:动态.
std::tie(strval0, std::ignore, std::ignore) = tp31;                       // 从tp31中取出第0个元素.
// 取值方式二:动态.
std::make_tuple(std::ref(strval0), std::ignore, std::ignore) = tp31;      // 从tp31中取出第0个元素.
// 取值方式三:静态.
std::cout << std::get<0>(tp31) << std::endl;
std::cout << std::get<1>(tp31) << std::endl;
std::cout << std::get<2>(tp31) << std::endl;
// std::get不支持以下遍历方式(i在编译期为不确定值):
//for (int i = 0; i < tp31._Mysize; i++)
//{
//	std::cout << "31 string: " << std::get<i>(tp31) << std::endl; 
//}
// 赋值方式一:动态
std::tie(strval0, svalue0, dblval0) = tp31;
strval0 = "tuple_modify";
tp31 = std::tie(strval0, svalue0, dblval0);
// 赋值方式二:动态
std::make_tuple(std::ref(strval0), std::ref(svalue0), std::ref(dblval0)) = tp31;
svalue0 = 200;
tp31 = std::make_tuple(std::ref(strval0), std::ref(svalue0), std::ref(dblval0));
// 赋值方式三:静态
std::get<1>(tp31) = 99;

/// tuple与pair的转化:
std::tuple<std::string, double> t1(std::pair<std::string, double>("stl", 0.1));           // 拷贝内存构造.
std::tuple<std::string, double> t2(std::move(std::pair<std::string, double>("stl", 0.1)));// 移动内存构造.
std::tuple<std::string, double> t3 = std::make_pair("stl", 0.1);                          // 拷贝内存赋值.
std::tuple<std::string, double> t4 = std::move(std::make_pair("stl", 0.1));               // 移动内存赋值.

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值