optional:

    optional库使用"容器"语义,包装了"可能产生无效值"的对象,实现了"未初始化"的概念.

#include <boost/optional.hpp>

using namespace boost;

"无意义"的值:

     函数并不总能返回有效的返回值,很多时候函数可能返回"无意义"的值,这不意味着函数执行失败,而是表明函数正确执行了,但结果却不是有用的值。
     表示返回值无意义最常用的做法是增加一个"哨兵"的角色,它位于解空间之外,如NULL,-1,EOF,string::npos,vector::end()等。但这些做法不够通用,而且很多时候不存在解空间之外的"哨兵".
     optional使用"容器"语义,为这种"无效值"的情形提供了一个较好的解决方案。
     optional很像一个仅能存放一个元素的容器,它实现了"未初始化"的概念:如果元素未初始化,那么容器就是空的,否则,容器内就是有效的,已经初始化的值。
     optional的真实接口很复杂,因为它要能够包装任何的类型。

操作函数:

    optional的模板类型参数T可以使任何类型,就如同一个标准容器对元素的要求,并不需要T具有缺省构造函数,但必须是可拷贝构造的。


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

      optional采用了指针语义来访问内部保存的元素,这使得optional未初始化时的行为就像一个空指针。它重载了operator*和operator->以实现与指针相同的操作,get()和get_ptr()可以以函数的操作形式获得元素的引用和指针。
       成员函数get_value_or(default)是一个特别的访问函数,可以保证返回一个有效的值,如果optional已初始化,那么返回内部的元素,否则返回default。
       optional也可以用隐式类型转换进行bool测试(用于条件判断),就像一个队指针的判断。
       optional还全面支持比较运算,包括==,!=,<,<,>,>=。与普通指针比较的"浅比较"(仅比较指针值)不同,optional的比较是"深比较",同时加入了对未初始化情况的判断。

用法:

      optional的接口简单明了,把它认为是一个大小为1并且行为类似指针的容器就可以了,或者把它想象成是一个类似scoped_ptr,shared_ptr的智能指针(注意,optional不是智能指针,用法类似但用途不同)。

代码示范:

#include <boost/optional.hpp>
using namespace boost;
using namespace std;
int main()
{
     optional<int> op0;                    //一个未初始化的optional对象
     optional<int> op1(boost::none);       //同上,使用none赋予未初始化值
     assert(!op0);
     assert(op0 == op1);
     assert(op1.get_value_or(253) == 253); //获取可选值

     optional<string> ops("test");         //初始化为字符串test
     string str = *ops;                    //用解引用操作符获取值
     cout <<str.c_str()<<endl;

     vector<int> v(10);
     optional<vector<int>&> opv(v);        //容纳一个容器的引用
     assert(opv);

     opv->push_back(5);                    //使用箭头操作符操纵容器
     assert(opv->size() == 11);
     opv = boost::none;
     assert(!opv);
     system("pause");
     return 0;
}

代码示范:

#include <math.h>
#include <boost/optional.hpp>
using namespace boost;
using namespace std;
optional<double> calc(int x)    //计算倒数
{
    return optional<double>(x != 0, 1.0 / x);//条件构造函数
}
optional<double> sqrt_op(double x)   //计算实数的平方根
{
    return optional<double>(x>0, sqrt(x));//条件构造函数
}
int main()
{
    optional<double> d = calc(10);
    if (d)
        cout << *d <<endl;
    d = sqrt_op(-10);
    if (!d)
        cout << "no result"<<endl;
    system("pause");
    return 0;
}

不使用boost::optional时的方法

#include <iostream>

int aaa()
{
    int i;
    std::cin >> i;
    return (i > 10) ? i : -1;
}

int main()
{
   int i = aaa();
   if (i != -1)
       std::cout << i << std::endl;
   else
       std::cout << "not valid input" << std::endl;
}

$ g++ m.cpp && ./a.out
11
11
[lane@Docker foo]$ ./a.out
2
not valid input

不使用boost::optional时的方法

#include <boost/optional.hpp>
#include <iostream>

boost::optional<int> aaa()
{
    int i;
    std::cin >> i;
    return (i > 10) ? i : boost::optional<int>{};
}

int main()
{
   boost::optional<int> i = aaa();
   if (i)
       std::cout << *i << std::endl;
   else
       std::cout << "not valid input" << std::endl;
}

$ g++ m.cpp && ./a.out
11
11
[lane@Docker foo]$ ./a.out
2
not valid input

工厂函数:

      optional提供一个类似make_pair(),make_shared()的工厂函数make_optional(),可以根据参数类型自动推导optional的类型,用来辅助创建optional对象,声明:

 
  1. optional<T> make_optional(T const &v);

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

      但make_optional()无法推导出T引用类型的optional对象,因此如果需要一个optional<T&>的对象,就不能使用make_optional()函数。

示例;

#include <boost/optional.hpp>
#include <boost/typeof/typeof.hpp>
using namespace boost;
using namespace std;
int main()
{
    BOOST_AUTO(x, make_optional(5));
    assert(*x == 5);


    BOOST_AUTO(y, make_optional<double>((*x > 10), 1.0));
    assert(!y);
    system("pause");
    return 0;
}


高级议题:

      optional<T>同STL容器一样,只提供基本的异常保证,不会超过被包装的类型T,它自身不抛出任何异常,只有在T构造时可能会抛出异常。

 

就地创建:

      optional<T>要求类型T具有拷贝语义,因为它内部会保存值的拷贝,但很多时候复杂对象的拷贝代价很高,而且这个值仅仅作为拷贝的临时用途,是一种浪费。

      因此optional库提供出了"就地创建"的概念,可以不要求类型具有拷贝语义,直接用构造函数所需的参数创建对象,这导致发展处了另一个Boost库--in_place_factory.


示范:

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


    system("pause");
    return 0;
}

引用类型:

     optional的模板参数类型可以使引用(T&),它在很多方面与原始类型T有不同,比如无法使用就地创建,就地赋值.与c++语言内置的引用类型不同的是,它可以声明时不指定初值,并且在赋值时转移包装的对象,而不是对原包装的对象赋值。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐