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

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

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

前言

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

variant的簡單使用

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

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

在示例程序中,定義了一個(gè) std::variant 對(duì)象 value 來存儲(chǔ)整型、浮點(diǎn)型和字符串類型中的任意一種,然后分別將 value 賦值為整型、浮點(diǎn)型和字符串類型,并使用 std::get 來獲取對(duì)應(yīng)的值,此時(shí)可以正常打印 value 對(duì)象中存儲(chǔ)的值

當(dāng)我們?cè)噲D將 value 賦值為其它未在定義變量時(shí)指定的類型時(shí),編譯器將會(huì)報(bào)編譯錯(cuò)誤,而當(dāng)我我們?cè)噲D獲取 value 中不存在的類型的值時(shí),程序?qū)?huì)拋出 std::bad_variant_access 異常,可以使用 try-catch 已經(jīng)捕獲。

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

variant相關(guān)函數(shù)和類

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

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

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

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ù)獲取對(duì)應(yīng)值的指針,如果類型不匹配會(huì)返回空指針

std::visit函數(shù)

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

自定義訪問結(jié)構(gòu)的寫法

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

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

overloaded是什么

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

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(jià),我們首先的得弄清楚lambda表達(dá)式是什么,在剖析lambda之前先來看看 std::visit 函數(shù)需要的參數(shù)是什么,分析std::visit的參數(shù),先看 struct VisitPackage 結(jié)構(gòu)更容易一些。

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

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

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

lambda 是什么

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

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

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

  func(7);

  return 0;
}

這是一個(gè)使用lambda表達(dá)式簡單的例子,代碼中定義了一個(gè)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)我們雖然定義了一個(gè)lambda函數(shù),但是編譯器為它生成了一個(gè)類 __lambda_4_15,生成了 int& 類型的構(gòu)造函數(shù),并實(shí)現(xiàn)了 operator操作符,再調(diào)用lambda函數(shù)時(shí)先生成了 __lambda_4_15類的對(duì)象,再調(diào)用類的 operator()函數(shù) func.operator()(7);,看到這里是不是似曾相識(shí),雖然還不是很明白,但是和struct VisitPackage的定義總是有種說不清道不明的血緣關(guān)系。

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

overloaded剖析

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

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

// 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()…; };

這是一個(gè)類模板的聲明,模板的名字是overloaded

分步拆解來看:

template<class… Ts> struct overloaded

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

struct overloaded : Ts…

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

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

  • 這是一個(gè)函數(shù)體內(nèi)的變長 using 聲明
  • 假設(shè) 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…>;

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

經(jīng)過這些解釋,我們可以認(rèn)為在最后一個(gè)例子中可能產(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;
}

總結(jié)

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

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

相關(guān)文章

最新評(píng)論