通用工具——键值对(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)); // 移动内存赋值.