optional庫使用"容器"語義杜顺,包裝了"可能產(chǎn)生無效值"的對(duì)象饺饭,實(shí)現(xiàn)了"未初始化"的概念物喷。
#include <boost/optional.hpp>
using namespace boost;
"無意義"的值:
函數(shù)并不總能返回有效的返回值惯悠,很多時(shí)候函數(shù)可能返回"無意義"的值邻邮,這不意味著函數(shù)執(zhí)行失敗,而是表明函數(shù)正確執(zhí)行了克婶,但結(jié)果卻不是有用的值饶囚。
表示返回值無意義最常用的做法是增加一個(gè)"哨兵"的角色,它位于解空間之外鸠补,如NULL萝风,-1,EOF紫岩,string::npos,vector::end()等规惰。但這些做法不夠通用,而且很多時(shí)候不存在解空間之外的"哨兵"泉蝌。
optional使用"容器"語義歇万,為這種"無效值"的情形提供了一個(gè)較好的解決方案。
optional很像一個(gè)僅能存放一個(gè)元素的容器勋陪,它實(shí)現(xiàn)了"未初始化"的概念:如果元素未初始化贪磺,那么容器就是空的,否則诅愚,容器內(nèi)就是有效的寒锚,已經(jīng)初始化的值。
optional的真實(shí)接口很復(fù)雜违孝,因?yàn)樗軌虬b任何的類型刹前。
操作函數(shù)
optional的模板類型參數(shù)T可以使任何類型,就如同一個(gè)標(biāo)準(zhǔn)容器對(duì)元素的要求雌桑,并不需要T具有缺省構(gòu)造函數(shù)喇喉,但必須是可拷貝構(gòu)造的。
可以有很多方式創(chuàng)建optional對(duì)象校坑,例如:
- 無參的optional()或者optional(boost::none)構(gòu)造一個(gè)未初始化optional對(duì)象拣技,參數(shù)boost::none是一個(gè)類似空指針的none_t類型常量千诬,表示未初始化。
- optional(v)構(gòu)造一個(gè)已初始化的optional對(duì)象膏斤,其值為v的拷貝徐绑。如果模板類型為T&,那么optional內(nèi)部持有對(duì)引用的包裝掸绞。
- optional(condition, v)根據(jù)條件condition來構(gòu)造optional對(duì)象,如果條件成立(true)則初始化為v耕捞,否則為未初始化衔掸。
- 此外optional還支持拷貝構(gòu)造和賦值操作,可以從另一個(gè)optional對(duì)象構(gòu)造俺抽。當(dāng)想讓一個(gè)optional對(duì)象重新恢復(fù)到未初始化狀態(tài)時(shí)敞映,可以向?qū)ο筚xnone值。
optional采用了指針語義來訪問內(nèi)部保存的元素磷斧,這使得optional未初始化時(shí)的行為就像一個(gè)空指針振愿。它重載了operator*和operator->以實(shí)現(xiàn)與指針相同的操作,get()和get_ptr()可以以函數(shù)的操作形式獲得元素的引用和指針弛饭。
成員函數(shù)get_value_or(default)是一個(gè)特別的訪問函數(shù)冕末,可以保證返回一個(gè)有效的值,如果optional已初始化侣颂,那么返回內(nèi)部的元素档桃,否則返回default。
optional也可以用隱式類型轉(zhuǎn)換進(jìn)行bool測(cè)試(用于條件判斷),就像一個(gè)隊(duì)指針的判斷憔晒。
optional還全面支持比較運(yùn)算藻肄,包括==,!=,<,<,>,>=拒担。與普通指針比較的"淺比較"(僅比較指針值)不同嘹屯,optional的比較是"深比較",同時(shí)加入了對(duì)未初始化情況的判斷从撼。
用法
optional的接口簡單明了州弟,把它認(rèn)為是一個(gè)大小為1并且行為類似指針的容器就可以了,或者把它想象成是一個(gè)類似scoped_ptr, shared_ptr的智能指針(注意低零,optional不是智能指針呆馁,用法類似但用途不同)。
代碼示范1:
#include <boost/optional.hpp>
using namespace boost;
using namespace std;
int main()
{
optional<int> op0; //一個(gè)未初始化的optional對(duì)象
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); //容納一個(gè)容器的引用
assert(opv);
opv->push_back(5); //使用箭頭操作符操縱容器
assert(opv->size() == 11);
opv = boost::none;
assert(!opv);
system("pause");
return 0;
}
代碼示范2:
#include <math.h>
#include <boost/optional.hpp>
using namespace boost;
using namespace std;
optional<double> calc(int x) //計(jì)算倒數(shù)
{
return optional<double>(x != 0, 1.0 / x);//條件構(gòu)造函數(shù)
}
optional<double> sqrt_op(double x) //計(jì)算實(shí)數(shù)的平方根
{
return optional<double>(x>0, sqrt(x));//條件構(gòu)造函數(shù)
}
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;
}
工廠函數(shù):
optional提供一個(gè)類似make_pair(),make_shared()的工廠函數(shù)make_optional(),可以根據(jù)參數(shù)類型自動(dòng)推導(dǎo)optional的類型浙滤,用來輔助創(chuàng)建optional對(duì)象,聲明:
optional<T> make_optional(T const &v);
optional<T> make_optional(bool condition, T const& v);
tips: 但make_optional()無法推導(dǎo)出T引用類型的optional對(duì)象气堕,因此如果需要一個(gè)optional<T&>的對(duì)象纺腊,就不能使用make_optional()函數(shù)畔咧。
代碼示例:
#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;
}
高級(jí)議題
異常處理
optional<T>同STL容器一樣,只提供基本的異常保證揖膜,不會(huì)超過被包裝的類型T誓沸,它自身不拋出任何異常,只有在T構(gòu)造時(shí)可能會(huì)拋出異常壹粟。
就地創(chuàng)建
optional<T>要求類型T具有拷貝語義拜隧,因?yàn)樗鼉?nèi)部會(huì)保存值的拷貝,但很多時(shí)候復(fù)雜對(duì)象的拷貝代價(jià)很高趁仙,而且這個(gè)值僅僅作為拷貝的臨時(shí)用途洪添,是一種浪費(fèi)。
因此optional庫提供出了"就地創(chuàng)建"的概念雀费,可以不要求類型具有拷貝語義干奢,直接用構(gòu)造函數(shù)所需的參數(shù)創(chuàng)建對(duì)象,這導(dǎo)致發(fā)展處了另一個(gè)Boost庫in_place_factory盏袄。
代理示例:
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
using namespace boost;
using namespace std;
int main()
{
//就地創(chuàng)建string對(duì)象忿峻,不需要臨時(shí)對(duì)象string("..")
optional<string> ops(in_place("test in_place_factory"));
cout<< (*ops).c_str()<<endl;
//就地創(chuàng)建std::vector對(duì)象,不需要臨時(shí)對(duì)象vector(10, 3)
optional<vector<int>> opp(in_place(10, 3));
assert(opp->size() == 10);
assert((*opp)[0] == 3);
system("pause");
return 0;
}
引用類型
optional的模板參數(shù)類型可以使用引用(T&),它在很多方面與原始類型T有不同辕羽,比如無法使用就地創(chuàng)建逛尚,就地賦值。與c++語言內(nèi)置的引用類型不同的是刁愿,它可以聲明時(shí)不指定初值黑低,并且在賦值時(shí)轉(zhuǎn)移包裝的對(duì)象,而不是對(duì)原包裝的對(duì)象賦值酌毡。