欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++的optional用法實例詳解

 更新時間:2024年02月27日 10:29:32   作者:leapmotion  
編程中我們可能會遇到要處理可能為空的變量,比如說容器,基本類型,或者說對象實例,下面通過實例代碼介紹C++的optional用法,感興趣的朋友一起看看吧

optional用法

1 問題引出

編程中我們可能會遇到要處理可能為空的變量,比如說容器,基本類型,或者說對象實例,我們簡單看個例子:

#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;
}

這里代碼很簡單,我們根據(jù)條件獲取vector中一個元素,這個元素是個結(jié)構(gòu)體,當(dāng)滿足條件時可以返回,但是沒有找到時仍然要返回一個對象,到我們main函數(shù)甚至要花一些力氣來判斷有沒有找到。如果沒有找到在getSome返回空就好了,這樣我們就來介紹optional

2 簡介

使用std::optional能夠達到上邊的效果,我們簡單了解下,首先optional是在c++17引入,可以看作是T類型和一個bool值的包裝。
關(guān)于std::optional可以接受對象或者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; // 不輸出
  }
}

我們看這個簡單的例子,pp用來存放int的對象,初始化為1,判斷pp是否包含值,可以輸出1,將nullopt賦值后,判斷時為false,自然也不會輸出。我們把上邊遺留的那個例子重新寫一下:

// 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對象填充,沒有時用nullopt填充,在main函數(shù)里判斷使用即可。

optional細則

創(chuàng)建optinal

有幾種方式創(chuàng)建optional,我們具體看下例子:

直接創(chuàng)建或者用nullopt賦值

std::optional<int> empty;
std::optional<int> opt = std::nullopt; 

使用對象初始化

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包含的對象

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)造:
其實使用std::in_place和使用std::make_optional 用法相近,都是原地構(gòu)造對象,避免使用對象初始化進行的一次拷貝等。std::in_place只是一個tag,用來表示我們使用std::optional的那個構(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òu)造函數(shù)參數(shù)都是以in_place_t類型為第一個參數(shù),就是表示一個占位符,后邊我們傳入要構(gòu)造對象的參數(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"};

寫起來要比std::make_optional簡便很多

optional的其他操作

/// 1
optional<int> opt {1};
opt.value(); // 1
*opt // 1
/// 2
optional<int> opt;
opt.value(); // 拋出異常
*opt // 為定義
opt.value_or(2); // 2(沒有值時使用默認值)
///3
optional<int> opt{2};
opt.emplace(4); // 重新構(gòu)造4的對象
opt.reset(); // 釋放掉原來的對象,nullopt

optional比較

和指針比較

大家是否在想指針是不是也可以達到這樣的效果,我們來看一下:

  • 如果我們和普通的指針相比,即用指針指向?qū)ο?,如果為空的時候使用nullptr來代替,對于我們第一個例子可以達到相似的效果,因為我們的vector的生命周期時在使用指針之后銷毀,因為指針只是簡單指向,對于指向已經(jīng)析構(gòu)的對象,無疑是一場災(zāi)難。
  • 如果和我們智能指針比較,例如第一個例子中,

第一種實現(xiàn)我們需要vector存放shared_ptr才能進行拷貝:

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;
}

實現(xiàn)起來有點繁瑣,并且還需要改動svec,這不妥。或者看起來這樣:

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引用計數(shù)為0的時候還是會做銷毀,這樣是錯誤的。
最后一種就是重新構(gòu)造一個Some對象,普通指針和智能都可以實現(xiàn)。普通指針需要做delete操作,如果用智能指針實現(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)這樣的角色,如何使用要看大家了,不過既然推出了新的標(biāo)準(zhǔn),而且如果要實現(xiàn)如此功能感覺還是optional使用起來方便一點,語義明確,而且代碼可讀性較好。

和rust的option比較

首先rust的option是一個枚舉:

enum Option<T> {
  Some(T),
  None,
} 

這個枚舉是個模版,枚舉中每個元素可以存放對象或者不存放,類似之前例子的rust的簡單實現(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致力于一個安全的語言,option是prelude,不需要顯示引入作用域,同樣不需要Option::前綴來直接使用Some和None,同時還配套和一些相關(guān)安全的函數(shù),看起來比C++的簡便一些,我們這里就做一個對比。??

參考

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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++實現(xiàn)視頻流轉(zhuǎn)換為圖片方式

    C++實現(xiàn)視頻流轉(zhuǎn)換為圖片方式

    今天小編就為大家分享一篇C++實現(xiàn)視頻流轉(zhuǎn)換為圖片方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-12-12
  • c++快速排序詳解

    c++快速排序詳解

    快速排序總體思想:先找到一個樞軸,讓他作為分水嶺,通過一趟排序?qū)⒋判虻挠涗浄指畛蓛刹糠?前面一部分都比樞軸小,后面一部分樞軸大,然后又分別對這兩部分記錄繼續(xù)進行遞歸的排序,達到整個序列有序的目的
    2017-05-05
  • C\C++ 獲取當(dāng)前路徑實例詳解

    C\C++ 獲取當(dāng)前路徑實例詳解

    這篇文章主要介紹了C\C++ 獲取當(dāng)前路徑實例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • VS2019創(chuàng)建C++工程的的實現(xiàn)步驟

    VS2019創(chuàng)建C++工程的的實現(xiàn)步驟

    本文主要介紹了VS2019創(chuàng)建C++工程步驟,包含新建項目、編輯文件、配置源文件目錄、編譯鏈接、輸出文件、設(shè)置斷點調(diào)試,具有一定的參考價值,感興趣的可以了解一下
    2024-12-12
  • C語言詳細講解strcpy strcat strcmp函數(shù)的模擬實現(xiàn)

    C語言詳細講解strcpy strcat strcmp函數(shù)的模擬實現(xiàn)

    這篇文章主要介紹了怎樣用C語言模擬實現(xiàn)strcpy與strcat和strcmp函數(shù),strcpy()函數(shù)是C語言中的一個復(fù)制字符串的庫函數(shù),strcat()函數(shù)的功能是實現(xiàn)字符串的拼接,strcmp()函數(shù)作用是比較字符串str1和str2是否相同
    2022-05-05
  • C++實現(xiàn)評教管理系統(tǒng)

    C++實現(xiàn)評教管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C++實現(xiàn)評教管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Visual Studio Code上添加小程序自動補全插件的操作方法

    Visual Studio Code上添加小程序自動補全插件的操作方法

    這篇文章主要介紹了Visual Studio Code上添加小程序自動補全插件的操作方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • c語言調(diào)用匯編的方法

    c語言調(diào)用匯編的方法

    在此記錄一下c調(diào)用匯編的方法,匯編使用的是AT&T語法。例子很簡單,就是在給一個整數(shù)用匯編轉(zhuǎn)換成二進制
    2013-11-11
  • C語言水仙花數(shù)的實現(xiàn)

    C語言水仙花數(shù)的實現(xiàn)

    這篇文章主要介紹了C語言水仙花數(shù)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 使用C語言繪制統(tǒng)計圖中的餅圖

    使用C語言繪制統(tǒng)計圖中的餅圖

    常用的統(tǒng)計圖有條形圖、柱形圖、折線圖、曲線圖、餅圖、環(huán)形圖、扇形圖,本文主要為大家詳細介紹了如何使用使用C語言繪制統(tǒng)計圖中的餅圖,希望對大家有所幫助
    2024-02-02

最新評論