1 optional的用法

optional类位于#include <boost/optional.hpp>中,包装了“可能阐释无效值”的对象,实现了“未初始化”的概念。函数并不能总是返回有意义的结果,有时候函数可能返回“无意义”的值,一般来说我们通常使用一个不再正常解空间的一个哨兵来表示无意义的概念,如NULL,-1,end()或者EOF.然后对于不同的应用场合,这些哨兵并不是通用的,而且有时候可能不存在这种解空间之外的哨兵。optional很像一个仅能存放一个元素的容器,它实现了"未初始化"的概念:如果元素未初始化,那么容器就是空的,否则,容器内就是有效的,已经初始化的值。optional的真实接口很复杂,因为它要能包装任何的类型。

2 操作方法

在具体的介绍之前,我们先看一个optional的一个使用示例。通过这个示例我们先对optional的使用有一个大体的认识:

#include <boost/optional.hpp>
#include <iostream>
#include <vector>
 
int main()
{
    boost::optional<int> op0;        //一个未初始化的optional对象
    boost::optional<int> op1(boost::none);        //同上,使用none赋予未初始化值
    assert(!op0);        //允许隐式类型转换为bool,用来判断是否为有效的值
    assert(op0==op1);    //未初始化的对象都是相同的
    assert(op1.get_value_or(253) == 253);        //获取可选值
 
    boost::optional<std::string> ops("test");        //使用test进行初始化
    std::string str = *ops;            //使用解引用操作获取赋予的值
    std::cout<<str<<std::endl;
 
    std::vector<int> v(10);
    boost::optional<std::vector<int>&> opv(v);        //容纳一个容器的引用
    assert(opv);            //已经使用v进行初始化了
    opv->push_back(5);        //使用箭头操作符操作其保存的容器
    assert(opv->size() == 11);
    opv = boost::none;        //重新是容器变为未初始化的
    assert(!opv);
    system("pause");        //程序暂停
    return 0;
}

从这个使用示例中,我们应该对optional的使用方式有一个大概的了解了。下面来对其构造函数和成员函数进行介绍。

2.1 构造函数

optional的构造函数用很多种:

  1. 无参的optional()或者optional(boost::none)构造一个未初始化optional对象,参数boost::none是一个类似空指针的none_t类型常量,表示未初始化。如上例中op0,op1的构造过程。
  2. optional(v)构造一个已初始化的optional对象,其值为v的拷贝(上例中ops的构造过程)。如果模板类型为T&,那么optional内部持有对引用的包装(上例中opv的构造过程)。
  3. optional(condition,v)根据条件condition来构造optional对象,如果条件成立(true)则初始化为v,否则为未初始化状态。
  4. optional还支持复制和赋值操作,可以从另外一个optional对象构造。当想让一个optional对象重新恢复到未初始化状态时,可以对该对象赋值为boost::none

2.2 成员函数

optional采用了指针语义来访问其保存的内部元素,因此我们可以将未初始化的optional对象的行为看作空指针。它重载了operator*operator->以实现与指针相同的语义。get()get_ptr()成员函数可以分别获取其保存的元素的引用和指针。成员函数get_value_or(default)是一个特别的访问函数,可以保证返回一个有效的值,如果optional已初始化,那么返回内部的元素,否则返回default(如上面示例所示,返回了默认值253)。
optional还定义了到bool的隐式类型转换,方便对其进行测试来判断一个optional对象是否为空。
optional还全面支持比较运算,包括==,!=,<,>,>=。与普通指针比较的“浅比较”(仅比较指针指)不同,optional的比较是""深比较",同时加入了对未初始化情况的判断。
关于optional的接口使用也简单明了,把它认为是一个大小为1并且行为类似指针的容器就可以。下面这个示例演示了如何使用optional对象作为函数的返回值来表示返回值是否有效的概念:

#include <boost/optional.hpp>
#include <iostream>
#include <cmath>
 
typedef boost::optional<double> optD;
 
optD Calc(int x)    //计算倒数
{
    return optD(!= 0,1.0/x);        //使用条件构造函数
}
 
optD SqrtOp(double x)    //计算实数的平方根
{
    return optD(x>0,sqrt(x));        //条件构造函数
}
 
int main()
{
    optD d = Calc(10);
    if(d)
        std::cout<<*d<<std::endl;
    d = SqrtOp(-10);
    if(!d)
        std::cout<<"no result"<<std::endl;
    system("pause");        //程序暂停
    return 0;
}

3 自动推导数据类型

在上面的示例中,optional保存的对象数据类型都比较简单而且我们事先都知道(如int,string).有时候我们可能很难知道optional到底要保存的对象的数据类型具体是什么,这时候如果能自动的推到出其保存对象的函数对象那么就十分方便了,optional提供了make_optional来实现了这种自动推到的机制,其函数声明如下:

optional<T> make_optional(const& v);
optional<T> make_optional(bool condition,const& v);

不过需要注意的是,make_optional()无法推到出T引用类型的optional对象(如本文第一个例子中vector引用对象),因此如果需要一个optional<T&>的对象,就不能使用make_optional函数。下面是其使用示例:

#include <boost/optional.hpp>
#include <iostream>
#include <boost/typeof/typeof.hpp>        //提供BOOST_AUTO
 
int main()
{
    BOOST_AUTO(x,boost::make_optional(5));        //BOOST_AUTO实现了标准库中auto的概念
    assert(*= 5);
    BOOST_AUTO(y,boost::make_optional(*x>10,1.0));
    //BOOST_AUTO(y, make_optional<double>((*x > 10), 1.0));        //也可以这样手动指定数据类型
    assert(!y);
    system("pause");        //程序暂停
    return 0;
}

4 就地创建

optional要求类型T具有拷贝语义,因为它内部会保存值的拷贝,但很多时候复杂对象的拷贝代价很高,而且这个值仅仅作为拷贝的临时用途,是一种浪费。因此optional库提供了"就地创建"的概念,可以不要求类型具有拷贝语义,直接用构造函数所需的参数创建对象,这导致发展出了另一个boost库——in_place_factory.

#include <boost/optional.hpp>
#include <iostream>
#include <vector>
#include <boost/utility/in_place_factory.hpp>        
 
int main()
{
    //就地创建string对象,不需要临时对象string("..")
    boost::optional<std::string> ops(boost::in_place("test in_place_factory"));
    std::cout<<*ops<<std::endl;
    //就地创建std::vector对象,不需要临时对象vector(10,3)
    boost::optional<std::vector<int>> opp(boost::in_place(10,3));
    assert(opp->size()==10);
    assert((*opp)[0] == 3);
    system("pause");        //程序暂停
    return 0;
}

上面的实例中vector作为容器元素创建时并没有使用引用的方式。optional的模板类型使用引用(T&)在很多方面与原始类型T有不同,比如无法使用上面的就地创建,就地赋值。与c++语言内置的引用类型不同的是,它可以声明是不指定初值,并且在赋值时转移包装的对象,而不是对源包装对象赋值。

参考文章:

boost optional

posted on 2017-11-07 12:27  学习时间轴  阅读(5947)  评论(0编辑  收藏  举报