boost I 容器与数据结构(三)

目录

六、tuple

1.std::pair

2.创建tuple对象

3.访问元素

4.比较操作

5.输入输出

6.联结变量

7.扩展

a.使用get_head 和get_tail遍历

b.tuples::element

c.tuples::length

d. 与C++标准库std::tuple的对比

七、any

1.访问元素

2.any的应用

3.与C++标准库对比


六、tuple

        tuple ( 元组)定义了一个有固定数目元素的容器,其中的每个元素类型都可以不相同,这与其他容器有着本质的区别(例如,标准容器 vector 或 array 虽然可以容纳很多元素,但所有的元素都必须是同一类型的)。

         tuple 很有用,它是 std::pair 的泛化,可以从函数返回任意数量的值,也可以代替struct组合数据。

        在拥有相同功能的情况下, tuple 的代码要比 struct 更简洁,而且 tuple 提供了许多立即可用( off-the-shelf ) 的操作元素的方法,使用起来更方便。而 struct 则必须手动定义所需的成员访问和操作函数,不仅麻烦,而且容易增加出错的概率和调试的时间。
        但由于 tuple 采用了模板技术,即使很少量的 tuple代码也会带来大量的模板实例化推演,从而导致编译时间增加(泛型编程的普遍现象)。因此,当使用 tuple 时,需要考虑编译的时间成本。但一般来说,在大型程序中编译 tuple 的时间只占很小的一部分,这部分时间造成的缺点完全可以被 tuple 带来的好处抵销。

1.std::pair

        标准库中提供的模板类 std::pair 是tuple的特例,2-tuple——仅持有两个成员的元组。std::pair是将2个数据组合成一个数据,如stl中的map就是将key和value放在一起来保存或者当一个函数需要返回2个数据的时候,可以选择pair。

        pair的实现是一个结构体,主要的两个成员变量是first、second。工厂函数 make_pair( )还可以根据参数自动推导类型,创建 pair 对象。

    std::pair<int, std::string> _pair0(0, "zero");
    std::pair<int, std::string> _pair1 = std::make_pair<int, std::string >(1, "one");
    auto _pair2 = std::make_pair(2, "second");//make_pair可以根据参数自动推导类型

    std::cout << _pair0.first << ":" << _pair0.second << std::endl;

2.创建tuple对象

        tuple默认最多支持 10 个模板类型参数,也就是说它最多能容纳 10 个不同类型的元素 ( 10- tuple )。tuple对元素的类型没有特殊的要求,但如果元素的类型不支持默认构造或赋值操作,那么 tuple 也会相应地缺失功能。元素也可以是 tuple,也就是说tuple支持嵌套定义。

        make_tuple( ):为了方便创建 tuple 对象, tuple 库提供与make_pair( )类似的 make_tuple( )函数,它同样可以根据参数类型推导出要创建的 tuple 类型,默认的类型是非引用类型。

        如果想让 make_tuple 生产的 tuple 数据类型是引用,那么要用到 ref 库的 ref ( ) 和cref ( ) 函数 , 它们可以包装变量 , 使 tuple 的类型为 T & 或 const T &,这在用于不可拷贝的数据类型时是很有用的。

#include <boost/tuple/tuple.hpp>
void TestTuple(){
    int i = 1;
    boost::tuple< int, std::string, bool > _tuple0(0, "zero", false);
    auto _tuple1 = boost::make_tuple(i, std::string("one"), true);//_tuple1类型<int,std::string,bool>
    auto _tuple2 = boost::make_tuple(boost::ref(i), std::string("one"), true);//_tuple2类型<int&,std::string,bool>
}

3.访问元素

        tuple提供成员函数 get( )访问内部的值。get( )是一个模板函数,它以整数为索引,返回tuple中的第 N 个元素。索引遵循 C ++ 的惯例,从0开始,如果索引超过了 tuple 内的元素个数则会导致编译错误。

        除了成员函数 get( ),tuple 库还提供一个自由函数 boost :: get ( ),它的用法与同名成员函数完全相同,只是需要接收一个 tuple 变量作为操作的对象。

注意:不能使用for循环那样使用get遍历tuple,因为get()是一个模板函数,编译实例化 时要求模板参数<int N>必须是编译期可确定的,而for循环中的变量i只能在运算时确定,因此无法编译模板代码。

    int _tuple0_num = _tuple0.get<0>();
    std::string _tuple0_name = _tuple0.get<1>();
    bool tuple0_bool = boost::get<2>(_tuple0);
    std::cout << "[" << _tuple0_num << "," << _tuple0_name << "," << tuple0_bool << "]" << std::endl;

4.比较操作

        tuple全面支持比较操作,包括相等和不等的各种测试,它将比较操作符转发到内部的各个元素进行比较,因此要求 tuple 的元素必须能够执行比较操作,否则会引发编译错误。作为比较对象的两个 tuple 也必须有相同的元素个数,否则也会引发编译错误。

        tuple的大小比较是基于“字典序”的,而且是“短路”操作,即从第一个元素开始,一旦得出比较结果就停止比较操作。

#include <boost/tuple/tuple_comparison.hpp>

std::cout << "_tuple0 大于 _tuple1吗?" << (_tuple0 > _tuple1) << std::endl;

 5.输入输出

        tuple支持 C ++ 标准库的流输入输出操作,与比较操作相同,对 tuple 内的所有元素逐个调用 operator< < 或 operator > > ,因此要求每个元素都支持流输入输出,否则也会引发编译错误。

        tuple的输入输出格式默认是用圆括号包围整个tuple,元素值间用空格分隔。

        tuple库还在名字空间 boost::tuples 里定义了 3个流格式操作符函数 , 用来改变输入输出的格式:

  • set open ( char ) : 设置 tuple 开始时的字符;
  • set close ( char ) : 设置 tuple 结束时的字符;
  • set delimiter ( char ) : 设置元素之间的分隔符。
    #include <boost/tuple/tuple_io.hpp>

    boost::tuple< int, std::string, bool > _tuple3(3, "three", false);
    std::cout << _tuple3 << std::endl;//(3 three 0)
    //std::cin >> _tuple3;//输入(5 five 1)
    std::cout << _tuple3 << std::endl;//(5 five 1)

    std::cout << boost::tuples::set_open('[');
    std::cout << boost::tuples::set_close(']');
    std::cout << boost::tuples::set_delimiter(',');
    std::cout << _tuple3 << std::endl;//[5,five,1]

6.联结变量

        tuple库提供一个类似 make_tuple( )的函数tie( ),正如其名,它可以把变量“联结”到tuple上,生成一个元素类型全是引用的 tuple ,相当于 make_tuple ( ref (a),ref (b),... ) ,可以被用于左值。

        tuples::ignore 相当于一个占位符,可以在赋值时“忽略”某些对象,当只关心 tuple 中少数元素值的时候就可以使用 tuples::ignore,从而不必书写一堆必须声明又不使用的变量。

{
    int age = 28;
    std::string name = "Lisa";
    boost::tuple<int&, std::string&> _tuple4 = boost::tie(age, name);
    std::cout << _tuple4 << std::endl;//[28,Lisa]
}

boost::tuple<int, std::string> TestTie()
{
    return boost::make_tuple(18, "Mary");
}
{
    //boost::tie被用于左值
    int age;
    std::string name;
    boost::tie(age, name) = TestTie();
    std::cout << age << "," << name << std::endl;//18,Mary

    //忽视某些对象(年龄)
    std::string name;
    boost::tie(boost::tuples::ignore, name) = TestTie();
}

7.扩展

a.使用get_head 和get_tail遍历

        tuple的内部结构是一个部件( cons )的链表,cons位于名字空间 boost::tuples。
通过 cons 的模板类型 head 和 tail , tuple 使用了类似operators库的基类链技术 ( 4 . 6 . 3 节 ) , 通过链式继承实现类型的容纳。tuple < int , double , string >它在概念上相当于:cons<int,cons<double,cons<string,null_type>>>,基类链的末尾是一个 nulltype , 它是一个空类 , 起到 " 哨兵 " 的作用。

        tuple利用 cons 提供了两个很有用的函数:get_head ( ) 和 get_tail ( ),它们分别返回 tuple的首元素和尾部链表的引用,使用它们可以递归访问 tuple 内的元素。

//PrintTuple是一个模板函数,因此可以接收任意的 tuple 对象
template<typename Tuple>
void PrintTuple(const Tuple& t)
{
    std::cout << t.get_head() << ",";
    PrintTuple(t.get_tail());
}
//为了使递归停止,我们必须有一个处理null_type的模板特化函数,只是用它来终止递归过程
template<>
void PrintTuple(const boost::tuples::null_type&){

}

//应用
{
    boost::tuple<int,int,std::string> _tuple5(1, 22,"Lisa");
    PrintTuple(_tuple5);
}

b.tuples::element

        element < N,T > ::type 可以给出 T(tuple 类型) 中第 N 个元素的类型。

   boost::tuples::element<2, boost::tuple<int, int, std::string>>::type str1;//std::string
   boost::tuples::element<1, decltype(_tuple5)>::type num;//int

c.tuples::length

        length < T >::value 可以给出 T 的元素数量,模板参数列表中的类型 T 是 tuple 类型。

   std::cout << boost::tuples::length<decltype(_tuple5)>::value << std::endl;//3

d. 与C++标准库std::tuple的对比

        现在 tuple 已经是C++标准库的一部分,但boost.tuple 并没有跟进标准及时更新,所以C++标准里的 std::tuple 与 boost::tuple 相比存在一些差异。

  • 支持右值引用和转移语义。
  • 使用可变参数模板,支持不限数量的类型。
  • 兼容 std::pair ,具有互操作性。
  • 提供 swap 成员函数,支持交换操作。
  • 提供新的 forward_as_tuple ( ) 和 tuple_cat ( )工厂函数。
  • 没有成员函数 get( )。
  • 元函数 tuple_element 和 tuple_size 分别对应boost::tuple 的 element 和 length 。

七、any

        any是一种很特殊的容器,它只能容纳一个元素,但这个元素可以是任意的类型(这些类型必须是可拷贝构造的,并且析构不能抛出异常)—— int 、 double 、string、标准容器或任何自定义类型,程序可以用any保存任意类型的数据,在任何需要的时候将它取出。 any 的这种功能与 shared_ptr < void > 有些类似,但 any 是类型安全的,它已经被收入 C ++17标准。

        如果容纳的元素类型是指针, any的析构函数并不会对指针执行 delete 操作,所以,如果用 any 保存原始指针会造成内存泄漏,替代方法是使用智能指针来存入 any 容器。不是所有的智能指针都可以作为 any 存储的对象,如 scoped_ptr 就不行,它不能被拷贝,不符合 any的类型要求,我们应该使用 shared _ ptr ,它是“最智能”的智能指针,几乎完全可以替代原始指针,而只需要很小的代价。

1.访问元素

        any类本身不提供任何对内部元素的访问函数,而是使用了一个友元函数 any_cast ( ) ,这个函数的命名模仿了标准库的转型操作符 xxx _ cast 。

        如果要转换的类型不是 any 内部对象的类型,或者any不持有任何值( empty( )= = true ),则这两个 any_cast ( )函数会抛出一个 bad_any_cast 异常。

#include <boost/any.hpp>
#include <boost/exception/diagnostic_information.hpp>
void TestAny()
{
    //创建any对象
    int a = 1;
    boost::any _any1(a);
    boost::any _any2("C++");
    //any_cast()访问元素
    try {
        boost::any_cast<int>(_any2);//类型不匹配
    }
    catch (boost::exception& e)
    {
        //Throw location unknown (consider using BOOST_THROW_EXCEPTION)
        //Dynamic exception type : struct boost::wrapexcept<class boost::bad_any_cast>
        //std::exception::what : boost::bad_any_cast : failed conversion using boost::any_cast
        std::cout << boost::current_exception_diagnostic_information()<< std::endl;
    }
    if (!_any1.empty()) {
        //any_cast
        int num = boost::any_cast<int>(_any1);//拷贝
        std::cout << num << std::endl;
        int num1 = boost::any_cast<int&>(_any1);//引用
        _any1.clear();
    }
    //存放指针时内存泄漏
    //int *p = new int(10);
    //boost::any _any3 = p;
    //正确方式
    std::shared_ptr<int> p(new int(10));
    boost::any _any3 = p;
}

2.any的应用

        any类可以用在那些要保存的值的类型是未知的应用程序,当容器的元素类型是 any 时,容器的表现就像一个可持有多个不同类型对象的动态 tuple。

std::vector<boost::any> _vector;

3.与C++标准库对比

        boost.any 已经较长时间没有更新了,所以与C ++17标准里的 any存在一些差异 , 主要有:

  • emplace ( ) 直接使用参数构造对象。
  • reset ( ) 直接清空对象。
  • has_value ( ) 检测 any 是否持有值,它是empty ( ) 的反义词。
  • 提供工厂函数 make_any ( ) 。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烫青菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值