0%

C++ std::visit介绍

见一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <variant>
#include <string>

struct MyVisitor
{
void operator()(double d) const {
std::cout << d << '\n';
}
void operator()(int i) const {
std::cout << i << '\n';
}
void operator()(const std::string& s) const {
std::cout << s << '\n';
}
};
int main()
{
std::variant<int, double, std::string> var1(42), var2(3.14), var3("visit");

std::visit(MyVisitor(), var1); // calls operator() for matching int type

std::visit(MyVisitor(), var2); // calls operator() for matching double type

std::visit(MyVisitor(), var3); // calls operator() for matching std::string type

return 0;

看官网给的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>

// the variant to visit
using var_t = std::variant<int, long, double, std::string>;

// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };

int main() {
std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
for (auto& v : vec) {
/// 1. void visitor, only called for side-effects(here, for I/O)
std::visit([](auto&& arg) {std::cout << arg; }, v);

/// 2. value-returning visitor, demonstrates the idiom of returning another variant
var_t w = std::visit([](auto&& arg)->var_t {return arg + arg; }, v);

/// 3. type-matching visitor: a lambda that that handles each type differently.
std::cout << ". After doubling, variant holds.";

std:visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>)
std::cout << "int with value " << arg << '\n';
else if constexpr (std::is_same_v<T, long>)
std::cout << "long with value " << arg << '\n';
else if constexpr (std::is_same_v<T, double>)
std::cout << "double with value " << arg << '\n';
else if constexpr (std::is_same_v<T, std::string>)
std::cout << "std::string with value " << arg << '\n';
else
static_assert(always_false_v<T>, "non-exhaustive visitor!");
}, w);
}

for (auto& v : vec) {
/// 4. another type-matching visitor: a class with 3 overloaded operator()'s
std::visit(overloaded{
[](auto arg) {std::cout << arg << ' '; },
[](double arg) {std::cout << std::fixed << arg << ' '; },
[](const std::string arg) {std::cout << std::quoted(arg) << ' '; },
}, v);
}

system("pause");
}

其中最为核心的代码片段为:

1
2
3
4
5
6
7
8
9
template<class... Fs> struct overload : Fs... { using Fs::operator()...; };
template<class... Fs> overload(Fs...) -> overload<Fs...>;

std::visit(overload
{
[](int x){ ... },
[](long x){ ... },
[](auto...){ ... }
}, v);

使用宏封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>

template<class... Fs> struct overload :Fs... {using Fs::operator()...; };
template<class... Fs> overload(Fs...)->overload<Fs...>;

template<class... Ts>
struct matcher {
std::tuple<Ts...> vs;
template<class... Vs> constexpr matcher(Vs&&... vs) : vs(std::forward<Vs>(vs)...) {}
template<class Fs> constexpr auto operator->*(Fs&& f) const {
auto curry = [&](auto&&... vs) {return std::visit(std::forward<Fs>(f), vs...); };
return std::apply(curry, std::move(vs));
}
};

template<class... Ts> matcher(Ts&&...)->matcher<Ts&&...>;
#define Match(...) matcher{__VA_ARGS__}->* overload

int main() {
std::vector<std::variant<int, double, std::string>> vec{1, 1.0, "ljslkfjskd"};
for (auto& it : vec) {
Match(it) {
[](auto&& x) {std::cout << "unknow type!" << std::endl; },
[](int x) {std::cout << "int: " << x << std::endl; },
[](double x) {std::cout << "double: " << x << std::endl; },
};
}
}