C++ std::visit介绍

2 分钟阅读

见一个简单的例子

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

看官网给的例子

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

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

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);

使用宏封装

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

标签:

分类:

更新时间: