C++17嘗鮮:variant


variant

variant 是 C++17 所提供的變體類型。variant<X, Y, Z> 是可存放 X, Y, Z 這三種類型數據的變體類型。

  • 與C語言中傳統的 union 類型相同的是,variant 也是聯合(union)類型。即 variant 可以存放多種類型的數據,但任何時刻最多只能存放其中一種類型的數據。
  • 與C語言中傳統的 union 類型所不同的是,variant 是可辨識的類型安全的聯合(union)類型。即 variant 無須借助外力只需要通過查詢自身就可辨別實際所存放數據的類型。

v = variant<int, double, std::string> ,則 v 是一個可存放 int, double, std::string 這三種類型數據的變體類型的對象。

  • v.index() 返回變體類型 v 實際所存放數據的類型的下標。
    變體中第1種類型下標為0,第2種類型下標為1,以此類推。
  • std::holds_alternative<T>(v) 可查詢變體類型 v 是否存放了 T 類型的數據。
  • std::get<I>(v) 如果變體類型 v 存放的數據類型下標為 I,那么返回所存放的數據,否則報錯。
    std::get_if<I>(&v) 如果變體類型 v 存放的數據類型下標為 I,那么返回所存放數據的指針,否則返回空指針。
  • std::get<T>(v) 如果變體類型 v 存放的數據類型為 T,那么返回所存放的數據,否則報錯。
    std::get_if<T>(&v) 如果變體類型 v 存放的數據類型為 T,那么返回所存放數據的指針,否則返回空指針。
#include <iostream>
#include <string>
#include <variant>

using namespace std;

int main()
{
    variant<int, double, string> v; // v == 0
    v = 1;
    bool has_int = holds_alternative<int>(v);
    bool has_double = holds_alternative<double>(v);
    cout << v.index() << has_int << has_double << get<0>(v) << *get_if<0>(&v) << endl; // 01011
    v = 2.0;
    cout << v.index() << (get_if<int>(&v) == nullptr) << get<1>(v) << get<double>(v) << endl; // 1122
    v = "a";
    cout << v.index() << get<2>(v) << get<string>(v) << endl; // 2aa
}

std::visit

std::visit(f, v) 將變體類型 v 所存放的數據作為參數傳給函數 f。
std::visit(f, v, u) 將變體類型 v, u 所存放的數據作為參數傳給函數 f。
...
std::visit 能將所有變體類型參數所存放的數據作為參數傳給函數參數。

#include <iostream>
#include <string>
#include <variant>
#include <boost/hana/functional/overload.hpp>

using namespace std;
namespace hana = boost::hana;
 
struct Visitor {
    void operator()(int n) const {
        cout << "int: " << n << endl;
    }
    void operator()(double d) const {
        cout << "double: " << d << endl;
    }
    void operator()(const string& s) const {
        cout << "string: " << s << endl;
    }
};

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
 
int main()
{
    variant<int, double, string> v; // v == 0
    auto f = [](auto& x) {cout << x << endl;};
    Visitor f2;
    overloaded f3{
        [](int n){cout << "int: " << n << endl;},
        [](double d){cout << "double: " << d << endl;},
        [](const string& s){cout << "string: " << s << endl;}
    };
    auto f4 = hana::overload(
        [](int n){cout << "int: " << n << endl;},
        [](double d){cout << "double: " << d << endl;},
        [](const string& s){cout << "string: " << s << endl;}
    );
    auto f5 = [](auto& arg) { using T = decay_t<decltype(arg)>; 
//  auto f5 = []<typename T>(T& arg) { // C++ 20
        if constexpr (is_same_v<T, int>) {
            cout << "int: " << arg << endl;
        }
        else if constexpr (is_same_v<T, double>) {
            cout << "double: " << arg << endl;
        }
        else if constexpr (is_same_v<T, string>) {
            cout << "string: " << arg << endl;
        }
    };
    v = 1; visit(f, v); visit(f2, v); visit(f3, v); visit(f4, v); visit(f5, v); // 1 int: 1 int: 1 int: 1 int: 1
    v = 2.0; visit(f, v); visit(f2, v); visit(f3, v); visit(f4, v); visit(f5, v); // 2 double: 2 double: 2 double: 2 double: 2
    v = "a"; visit(f, v); visit(f2, v); visit(f3, v); visit(f4, v); visit(f5, v); // a string: a string: a string: a string: a
}
  • f 和 f5 是泛型 lambda,接受所有參數的類型。
    f 不分辨參數類型。
    f5 通過編譯期 if 語句來分辨參數類型。
  • f2 和 f3 是函數對象,通過重載函數調用操作符來分辨參數的類型。
    f2 的函數調用操作符由自身定義。
    f3 的函數調用操作符繼承自3個lambda。
  • f4 這個函數對象經由 boost::hana::overload 函數生成,該函數所生成的函數對象能從多個lambda參數中選取一個合適的來調用指定參數。
  • 這段代碼總共使用了三個C++17的新特性:
    適用於類模板的自動推斷向導 https://www.cnblogs.com/zwvista/p/7748363.html
    變長 using 聲明 https://www.cnblogs.com/zwvista/p/9256655.html
    編譯期 if 語句 https://www.cnblogs.com/zwvista/p/9238273.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM