C++的optional用法實(shí)例詳解
optional用法
1 問(wèn)題引出
編程中我們可能會(huì)遇到要處理可能為空的變量,比如說(shuō)容器,基本類型,或者說(shuō)對(duì)象實(shí)例,我們簡(jiǎn)單看個(gè)例子:
#include <string>
#include <vector>
#include <memory>
struct Some
{
int some_i_ = 0;
std::string some_str_;
};
Some
getSome(const std::vector<Some>& svec,
int i)
{
auto iter =
std::find_if(svec.begin(), svec.end(),
[i](const Some& s) {
return s.some_i_ == i;
}
);
if (iter != svec.end()) {
return *iter;
}
return Some();
}
int main()
{
std::vector<Some> someVec;
someVec.push_back({1, "1"});
Some s = getSome(someVec, 1);
s = getSome(someVec, 2);
return 0;
}這里代碼很簡(jiǎn)單,我們根據(jù)條件獲取vector中一個(gè)元素,這個(gè)元素是個(gè)結(jié)構(gòu)體,當(dāng)滿足條件時(shí)可以返回,但是沒(méi)有找到時(shí)仍然要返回一個(gè)對(duì)象,到我們main函數(shù)甚至要花一些力氣來(lái)判斷有沒(méi)有找到。如果沒(méi)有找到在getSome返回空就好了,這樣我們就來(lái)介紹optional
2 簡(jiǎn)介
使用std::optional能夠達(dá)到上邊的效果,我們簡(jiǎn)單了解下,首先optional是在c++17引入,可以看作是T類型和一個(gè)bool值的包裝。
關(guān)于std::optional可以接受對(duì)象或者nullopt(表示為空值),參考一段例子:
#include <iostream>
#include <optional>
using namespace std;
int main()
{
std::optional<int> pp = 1;
if (pp) {
cout << *pp << endl; // 1
}
pp = nullopt;
if (pp) {
cout << *pp << endl; // 不輸出
}
}我們看這個(gè)簡(jiǎn)單的例子,pp用來(lái)存放int的對(duì)象,初始化為1,判斷pp是否包含值,可以輸出1,將nullopt賦值后,判斷時(shí)為false,自然也不會(huì)輸出。我們把上邊遺留的那個(gè)例子重新寫一下:
// snip...
#include <iostream>
using namespace std;
optional<Some>
getSome(const std::vector<Some>& svec, int i)
{
auto iter = std::find_if(svec.begin(), svec.end(), [i](const Some& s) {
return s.some_i_ == i;
});
if (iter != svec.end()) {
return *iter;
}
return nullopt;
}
int main()
{
vector<Some> someVec;
someVec.push_back({1, "11"});
auto s_ptr = getSome(someVec, 1);
if (s_ptr) {
cout << s_ptr->some_str_ << endl; // “11”
}
s_ptr = getSome(someVec, 2);
if (s_ptr) {
cout << s_ptr->some_str_ << endl; // 不輸出
}
return 0;
}我們把getSome的返回值的類型改為用optional包裝,如果滿足條件用Some對(duì)象填充,沒(méi)有時(shí)用nullopt填充,在main函數(shù)里判斷使用即可。
optional細(xì)則
創(chuàng)建optinal
有幾種方式創(chuàng)建optional,我們具體看下例子:
直接創(chuàng)建或者用nullopt賦值
std::optional<int> empty; std::optional<int> opt = std::nullopt;
使用對(duì)象初始化
std::optional<int> opt = 1;
struct Some
{
int some_i_ = 0;
std::string some_str_;
};
Some s;
std::optional<Some> opt = s;使用 std::make_optional構(gòu)造,類似std::make_shared可以傳遞參數(shù)原地構(gòu)造optional包含的對(duì)象
struct Some
{
Some(int i, std::string str):
some_i_(i),
some_str_(std::move(str)) {}
int some_i_ = 0;
std::string some_str_;
};
using namespace std;
optional<Some> opt = make_optional<Some>(1, "1");
auto opt = make_optional(1); // optional<int>使用std::in_place構(gòu)造:
其實(shí)使用std::in_place和使用std::make_optional 用法相近,都是原地構(gòu)造對(duì)象,避免使用對(duì)象初始化進(jìn)行的一次拷貝等。std::in_place只是一個(gè)tag,用來(lái)表示我們使用std::optional的那個(gè)構(gòu)造函數(shù)。
optional的構(gòu)造函數(shù)是這樣:
//
template <class... _Args, class = enable_if_t<
is_constructible_v<value_type, _Args...>>>
constexpr explicit
optional(in_place_t, _Args&&... __args)
: __base(in_place, _VSTD::forward<_Args>(__args)...) {}
//
template <class _Up, class... _Args, class = enable_if_t<
is_constructible_v<value_type, initializer_list<_Up>&, _Args...>>>
constexpr explicit
optional(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
: __base(in_place, __il, _VSTD::forward<_Args>(__args)...) {}
這里兩個(gè)構(gòu)造函數(shù)參數(shù)都是以in_place_t類型為第一個(gè)參數(shù),就是表示一個(gè)占位符,后邊我們傳入要構(gòu)造對(duì)象的參數(shù)。我們參考例子:
struct Some
{
Some(int i, std::string str):
some_i_(i),
some_str_(std::move(str)) {}
int some_i_ = 0;
std::string some_str_;
};
using namespace std;
optional<Some> opt {in_place, 1, "1"};寫起來(lái)要比std::make_optional簡(jiǎn)便很多
optional的其他操作
/// 1
optional<int> opt {1};
opt.value(); // 1
*opt // 1
/// 2
optional<int> opt;
opt.value(); // 拋出異常
*opt // 為定義
opt.value_or(2); // 2(沒(méi)有值時(shí)使用默認(rèn)值)
///3
optional<int> opt{2};
opt.emplace(4); // 重新構(gòu)造4的對(duì)象
opt.reset(); // 釋放掉原來(lái)的對(duì)象,nulloptoptional比較
和指針比較
大家是否在想指針是不是也可以達(dá)到這樣的效果,我們來(lái)看一下:
- 如果我們和普通的指針相比,即用指針指向?qū)ο?,如果為空的時(shí)候使用nullptr來(lái)代替,對(duì)于我們第一個(gè)例子可以達(dá)到相似的效果,因?yàn)槲覀兊膙ector的生命周期時(shí)在使用指針之后銷毀,因?yàn)橹羔樦皇呛?jiǎn)單指向,對(duì)于指向已經(jīng)析構(gòu)的對(duì)象,無(wú)疑是一場(chǎng)災(zāi)難。
- 如果和我們智能指針比較,例如第一個(gè)例子中,
第一種實(shí)現(xiàn)我們需要vector存放shared_ptr才能進(jìn)行拷貝:
shared_ptr<Some> getSome(
const vector<shared_ptr<Some>>& svec,
int i)
{
auto iter = std::find_if(svec.begin(), svec.end(), [i](const Some& s) {
return s.some_i_ == i;
});
if (iter != svec.end()) {
return *iter;
}
return nullptr;
}實(shí)現(xiàn)起來(lái)有點(diǎn)繁瑣,并且還需要改動(dòng)svec,這不妥?;蛘呖雌饋?lái)這樣:
shared_ptr<Some>
getSome(const vector<Some>& svec, int i)
{
auto iter = std::find_if(svec.begin(), svec.end(), [i](const Some& s) {
return s.some_i_ == i;
});
if (iter != svec.end()) {
Some s = *iter;
return shared_ptr<Some>{&s};
}
return nullptr;
}這樣就和我們使用普通指針是一樣的,并且shared_ptr引用計(jì)數(shù)為0的時(shí)候還是會(huì)做銷毀,這樣是錯(cuò)誤的。
最后一種就是重新構(gòu)造一個(gè)Some對(duì)象,普通指針和智能都可以實(shí)現(xiàn)。普通指針需要做delete操作,如果用智能指針實(shí)現(xiàn)也可以:
shared_ptr<Some>
getSome(const vector<Some>& svec, int i)
{
auto iter = std::find_if(svec.begin(), svec.end(),
[i](const Some& s) {
return s.some_i_ == i;
});
if (iter != svec.end()) {
return std::make_shared<Some>(*iter);
}
return nullptr;
}我們發(fā)現(xiàn)智能指針也可以充當(dāng)這樣的角色,如何使用要看大家了,不過(guò)既然推出了新的標(biāo)準(zhǔn),而且如果要實(shí)現(xiàn)如此功能感覺(jué)還是optional使用起來(lái)方便一點(diǎn),語(yǔ)義明確,而且代碼可讀性較好。
和rust的option比較
首先rust的option是一個(gè)枚舉:
enum Option<T> {
Some(T),
None,
} 這個(gè)枚舉是個(gè)模版,枚舉中每個(gè)元素可以存放對(duì)象或者不存放,類似之前例子的rust的簡(jiǎn)單實(shí)現(xiàn):
fn getSome(b: bool) -> Option<i32> {
if b {
return Some(3);
}
return None;
}
fn main() {
let b = false;
if let Some(s) = getSome(b) {
println!("hello.. {}", s);
}
else {
println!("hello.. null");
}
}getSome如果滿足條件返回Some,不滿足返回None。
rust致力于一個(gè)安全的語(yǔ)言,option是prelude,不需要顯示引入作用域,同樣不需要Option::前綴來(lái)直接使用Some和None,同時(shí)還配套和一些相關(guān)安全的函數(shù),看起來(lái)比C++的簡(jiǎn)便一些,我們這里就做一個(gè)對(duì)比。??
參考
https://en.cppreference.com/w/cpp/utility/optional/optional
https://kaisery.gitbooks.io/trpl-zh-cn/content/ch06-01-defining-an-enum.html
到此這篇關(guān)于C++的optional解析的文章就介紹到這了,更多相關(guān)C++ optional內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
C++學(xué)習(xí)之算術(shù)運(yùn)算符使用詳解
C++高級(jí)數(shù)據(jù)結(jié)構(gòu)之優(yōu)先隊(duì)列

