optional的使用


 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM