亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++訪問std::variant類型數(shù)據(jù)的幾種方式小結

 更新時間:2024年02月20日 15:06:14   作者:AlbertS  
std::variant是?C++17中引入的一個新的類模板,提供了一種存儲不同類型的值的方式,本文主要介紹了C++訪問std::variant類型數(shù)據(jù)的幾種方式小結,具有一定的參考價值,感興趣的可以了解一下

前言

std::variant(可變體) 是 C++17 中引入的一個新的類模板,提供了一種存儲不同類型的值的方式,類似于之前版本中的 union(聯(lián)合體),但可以存儲非 POD 類型和類對象,能夠在運行時進行類型檢查和轉換,但具有更多的功能和更高的類型安全性,今天來看一下存儲在std::variant中的數(shù)據(jù)要怎么讀取。

variant的簡單使用

可以參考cppreference網(wǎng)站的使用示例,也可以看看下面這個例子:

#include <iostream>
#include <variant>
#include <string>

int main()
{
    std::variant<int, double, std::string> value;

    value = 110;
    std::cout << "The value is an integer: " << std::get<int>(value) << std::endl;

    value = 0.618;
    std::cout << "The value is a double: " << std::get<double>(value) << std::endl;

    value = "hello world";
    std::cout << "The value is a string: " << std::get<std::string>(value) << std::endl;

    // value = true;        // Compilation error: cannot convert type bool to any of the alternative types
    // std::get<int>(value) // std::bad_variant_access exception: value holds a different type

    return 0;
}

在示例程序中,定義了一個 std::variant 對象 value 來存儲整型、浮點型和字符串類型中的任意一種,然后分別將 value 賦值為整型、浮點型和字符串類型,并使用 std::get 來獲取對應的值,此時可以正常打印 value 對象中存儲的值

當我們試圖將 value 賦值為其它未在定義變量時指定的類型時,編譯器將會報編譯錯誤,而當我我們試圖獲取 value 中不存在的類型的值時,程序將會拋出 std::bad_variant_access 異常,可以使用 try-catch 已經(jīng)捕獲。

通過這段代碼我們可以得知,使用std::variant可以方便地存儲多種類型的數(shù)據(jù),并且能夠在運行時進行類型檢查和轉換,這使得代碼更加清晰易讀,便于維護。

variant相關函數(shù)和類

  • 成員函數(shù)
    • index:返回 variant 中保存用類型的索引下標
    • valueless_by_exception:返回 variant 是否處于因異常導致的無值狀態(tài)
    • emplace:原位構造 variant 中的值
  • 非成員函數(shù)
    • visit:通過調用variant保存類型值所提供的函數(shù)對象獲取具體值
    • holds_alternative:檢查某個 variant 是否當前持有某個給定類型
    • std::get:以給定索引或類型讀取 variant 的值,錯誤時拋出異常
    • get_if:以給定索引或類型,獲得指向被指向的 variant 的值的指針,錯誤時返回空指針
  • 輔助類
    • monostate:用作非可默認構造類型的 variant 的首個可選項的占位符類型(預防一些類型不提供無參構造函數(shù))
    • bad_variant_access:非法地訪問 variant 的值時拋出的異常
    • variant_npos:非法狀態(tài)的 variant 的下標

訪問std::variant數(shù)據(jù)

從前面提到的例子和函數(shù)說明,我們可以看到有多種方式來訪問std::variant數(shù)據(jù),接一下來一起總結一下:

std::get搭配index函數(shù)使用

#include <iostream>
#include <variant>

int main()
{
    std::variant<double, int> value = 119;

    if (1 == value.index())
        std::cout << "The value is: " << std::get<1>(value) << std::endl;

    return 0;
}

先用 index() 查詢 variant保存的類型索引,然后在通過 std::get<NUMBER>() 獲取其中的值

std::get搭配std::holds_alternative函數(shù)使用

#include <iostream>
#include <variant>

int main()
{
    std::variant<double, int> value = 119;

    if (std::holds_alternative<int>(value))
        std::cout << "The value is: " << std::get<int>(value) << std::endl;

    return 0;
}

先通過 std::holds_alternative() 查詢 variant保存的類型,然后在通過 std::get<TYPE>() 獲取其中的值

std::get_if函數(shù)

#include <iostream>
#include <variant>

int main()
{
    std::variant<double, int> value = 119;

    if (auto p = std::get_if<int>(&value))
        std::cout << "The value is: " << *p << std::endl;

    return 0;
}

直接使用 std::get_if 函數(shù)獲取對應值的指針,如果類型不匹配會返回空指針

std::visit函數(shù)

使用函數(shù)visit函數(shù)訪問時,有點像使用std::sort這類函數(shù),可以搭配自定義的結構(排序)重寫operator(),讓其變成可以被調用的函數(shù)對象,也可以定義lambda自帶可執(zhí)行特性。

自定義訪問結構的寫法

#include <iostream>
#include <variant>

int main()
{
    std::variant<double, int> value = 119;

    struct VisitPackage
    {
        auto operator()(const double& v) { std::cout << "The value is: " << v << std::endl; }
        auto operator()(const int& v) { std::cout << "The value is: " << v << std::endl; }
    };

    std::visit(VisitPackage(), value);

    return 0;
}

定義lambda函數(shù)組重載

#include <iostream>
#include <variant>

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

int main()
{
    std::variant<double, int> value = 119;

    std::visit(overloaded {
      [] (const double& v) { std::cout << "The value is: " << v << std::endl; },
      [] (const int& v) { std::cout << "The value is: " << v << std::endl; }
    }, value);

    return 0;
}

這種方式將多個lambda放到一起形成重載,進而達到了訪問variant數(shù)據(jù)的目的。

overloaded是什么

上文例子中的最后一個中使用到了 overloaded,這令人眼花繚亂的寫法著實很詭異,不過我們可以從頭來分析一下,最后兩個例子中等價的兩部分是

struct VisitPackage
{
    auto operator()(const double& v) { std::cout << "The value is: " << v << std::endl; }
    auto operator()(const int& v) { std::cout << "The value is: " << v << std::endl; }
};

VisitPackage()

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

overloaded {
    [] (const double& v) { std::cout << "The value is: " << v << std::endl; },
    [] (const int& v) { std::cout << "The value is: " << v << std::endl; }
}

要想理解它們?yōu)槭裁吹葍r,我們首先的得弄清楚lambda表達式是什么,在剖析lambda之前先來看看 std::visit 函數(shù)需要的參數(shù)是什么,分析std::visit的參數(shù),先看 struct VisitPackage 結構更容易一些。

std::visit的第一個參數(shù)

通俗的來說std::visit的第一個參數(shù)需要的是一個可執(zhí)行的對象,如果對象能被執(zhí)行就需要實現(xiàn) operator() 這個操作符,看起來像函數(shù)一樣,這就是為什么在 struct VisitPackage 中定義了 operator(),并且定義了兩個形成了參數(shù)不同的靜態(tài)重載,作用就是為了在訪問 variant 對象時適配不同的類型,在訪問variant 對象時會選擇最匹配的 operator() 函數(shù),進而實現(xiàn)了訪問variant中不同類型值行為不同的目的。

那lambda表達式能實現(xiàn)這個目的嗎?我們接著往下看

lambda 是什么

自從 C++11 引入lambda之后,對它贊美的聲音不絕于耳,那lambda表達式究竟是怎樣實現(xiàn)的呢?真的就是一個普通的函數(shù)嗎?我們看一個小例子:

int main() {
  int x = 5, y = 6;

  auto func = [&](int n) {
    return x + n;
  };

  func(7);

  return 0;
}

這是一個使用lambda表達式簡單的例子,代碼中定義了一個int類型參數(shù)的返回值也是int的lambda函數(shù),作用就是將外部變量x與函數(shù)參數(shù)的和返回,我們使用 cppinsights.io 網(wǎng)站來將此段代碼展開

int main()
{
    int x = 5;
    int y = 6;

    class __lambda_4_15
    {
    public:
        inline /*constexpr */ int operator()(int n) const
        {
            return x + n;
        }

    private:
        int & x;

    public:
        __lambda_4_15(int & _x)
        : x{_x}
        {}
    };

    __lambda_4_15 func = __lambda_4_15{x};
    func.operator()(7);
    return 0;
}

可以發(fā)現(xiàn)我們雖然定義了一個lambda函數(shù),但是編譯器為它生成了一個類 __lambda_4_15,生成了 int& 類型的構造函數(shù),并實現(xiàn)了 operator操作符,再調用lambda函數(shù)時先生成了 __lambda_4_15類的對象,再調用類的 operator()函數(shù) func.operator()(7);,看到這里是不是似曾相識,雖然還不是很明白,但是和struct VisitPackage的定義總是有種說不清道不明的血緣關系。

弄清楚了lambda函數(shù)的本質,現(xiàn)在要實現(xiàn)的是怎么把多個lambda函數(shù)合成一個對象,并且讓他們形成重載,因為lambda函數(shù)本質上存在一個類,只需要定義一個子類,繼承多個lambda表達式就可以了,其實 overloaded 這個模板類就是為了實現(xiàn)這個目的。

overloaded剖析

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

一時間看起來很難理解,它來自 en.cppreference.com 中介紹 std::visit 訪問 std::variant 的例子,可以換行看得更清楚一點:

// helper type for the visitor #4
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

template<class… Ts> struct overloaded : Ts… { using Ts::operator()…; };

這是一個類模板的聲明,模板的名字是overloaded

分步拆解來看:

template<class… Ts> struct overloaded

  • 表示類的模板參數(shù)為可變長的參數(shù)包 Ts
  • 假設 Ts 包含 T1, T2, … , TN,那么這一句聲明可以展開為:template<class T1, class T2, …, class TN> struct overloaded

struct overloaded : Ts…

  • 表示類的基類為參數(shù)包 Ts 內(nèi)所有的參數(shù)類型
  • 假設 Ts 包含 T1, T2, … , TN,那么這一句聲明可以展開為:struct overloaded : T1, T2, …, TN

{ using Ts::operator()…; };

  • 這是一個函數(shù)體內(nèi)的變長 using 聲明
  • 假設 Ts 包含 T1, T2, … , TN,那么這一句聲明可以展開為:{ using T1::operator(), T1::operator(), …, TN::operator(); }
  • 經(jīng)過這步聲明,overloaded 類的參數(shù)包 Ts 內(nèi)所有的參數(shù)作為基類的成員函數(shù)operator()均被 overloaded 類引入了自己的作用域

template<class… Ts> overloaded(Ts…) -> overloaded<Ts…>;

  • 這是一個自動推斷向導說明,用于幫助編譯器根據(jù) overloaded 構造器參數(shù)的類型來推導 overloaded 的模板參數(shù)類型,C++17需要,C++20已不必寫
  • 它告訴編譯器,如果 overloaded 構造器所有參數(shù)的類型的集合為Ts,那么 overloaded 的模板參數(shù)類型就是 Ts 所包含的所有類型
  • 如果表達式a1, a2, …, an的類型分別為T1, T2, …, TN,那么構造器表達式overloaded x{a1, a2, …, an} 推導出,overloaded的類型就是 overloaded<T1, T2, …, TN>

經(jīng)過這些解釋,我們可以認為在最后一個例子中可能產(chǎn)生了類似這樣的代碼:

#include <iostream>
#include <variant>

class __lambda_12_7
{
public:
    inline /*constexpr */ void operator()(const double & v) const
    {
        std::operator<<(std::cout, "The value is: ").operator<<(v).operator<<(std::endl);
    }
};

class __lambda_13_7
{
public:
    inline /*constexpr */ void operator()(const int & v) const
    {
        std::operator<<(std::cout, "The value is: ").operator<<(v).operator<<(std::endl);
    }
};

template<>
struct overloaded<__lambda_12_7, __lambda_13_7> : public __lambda_12_7, public __lambda_13_7
{
    using __lambda_12_7::operator();
    // inline /*constexpr */ void ::operator()(const double & v) const;

    using __lambda_13_7::operator();
    // inline /*constexpr */ void ::operator()(const int & v) const;
};

int main()
{
    std::variant<double, int> value = std::variant<double, int>(119);

    std::visit(overloaded{__lambda_12_7(__lambda_12_7{}), __lambda_13_7(__lambda_13_7{})}, value);
    return 0;
}

總結

  • std::variant 可以存儲多個類型的值,并且它會自動處理類型轉換和內(nèi)存分配
  • std::variant 可以存儲非 POD 類型和類對象,能夠在運行時進行類型檢查和轉換,具有更高的類型安全性
  • 可以使用 std::visit 全局函數(shù)來訪問 std::variant 中存儲的值,該函數(shù)根據(jù)存儲的值的類型自動選擇調用哪個函數(shù)對象
  • 可以使用 std::holds_alternative 函數(shù)來檢查變量中是否存儲了特定的類型
  • 定義lambda函數(shù)時,編譯器會為其生成一個類

 到此這篇關于C++訪問std::variant類型數(shù)據(jù)的幾種方式小結的文章就介紹到這了,更多相關C++訪問std::variant內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • C語言掃雷游戲的實現(xiàn)方法

    C語言掃雷游戲的實現(xiàn)方法

    這篇文章主要為大家詳細介紹了C語言掃雷游戲的實現(xiàn)方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C語言實現(xiàn)電影院選座管理系統(tǒng)

    C語言實現(xiàn)電影院選座管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)電影院選座管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • 關于C++繼承你可能會忽視的點

    關于C++繼承你可能會忽視的點

    繼承是面向對象三大特性之一,有些類與類之間存在特殊的關系,下面這篇文章主要給大家介紹了關于C++繼承你可能會忽視的點,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-02-02
  • MFC之ComboBox控件用法實例教程

    MFC之ComboBox控件用法實例教程

    這篇文章主要介紹了MFC之ComboBox控件用法,包括了ComboBox控件常見的各類用法,非常具有實用價值,需要的朋友可以參考下
    2014-09-09
  • C++ 數(shù)據(jù)結構實現(xiàn)兩個棧實現(xiàn)一個隊列

    C++ 數(shù)據(jù)結構實現(xiàn)兩個棧實現(xiàn)一個隊列

    這篇文章主要介紹了詳解C++ 數(shù)據(jù)結構實現(xiàn)兩個棧實現(xiàn)一個隊列的相關資料,需要的朋友可以參考下
    2017-03-03
  • Visual?Studio?2022?安裝低版本?.Net?Framework的圖文教程

    Visual?Studio?2022?安裝低版本?.Net?Framework的圖文教程

    這篇文章主要介紹了Visual?Studio?2022?如何安裝低版本的?.Net?Framework,首先打開?Visual?Studio?Installer?可以看到vs2022?只支持安裝4.6及以上的版本,那么該如何安裝4.6以下的版本,下面將詳細介紹,需要的朋友可以參考下
    2022-09-09
  • C語言實現(xiàn)賓館管理系統(tǒng)課程設計

    C語言實現(xiàn)賓館管理系統(tǒng)課程設計

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)賓館管理系統(tǒng)課程設計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C++實現(xiàn)關系與關系矩陣的代碼詳解

    C++實現(xiàn)關系與關系矩陣的代碼詳解

    這篇文章主要介紹了C++實現(xiàn)關系與關系矩陣,功能實現(xiàn)包括關系的矩陣表示,關系的性質判斷及關系的合成,本文結合示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • C++類模板與模板類深入詳解

    C++類模板與模板類深入詳解

    這篇文章主要介紹了C++類模板與模板類深入詳解,需要的朋友可以參考下
    2014-07-07
  • C語言實現(xiàn)停車管理系統(tǒng)

    C語言實現(xiàn)停車管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)停車管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評論