C++17嚐鮮:variant
阿新 • • 發佈:2019-02-15
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.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引數中選取一個合適的來呼叫指定引數。