使用 std::variant 作为 class 会员并申请访问者

Use std::variant as class member and apply visitor

我正在尝试将std::variant用作class成员变量,然后使用运算符重载,以便此class的两个Variants可以使用运算符plus 产生一个新的变量。问题是 std::get 不像我想的那样工作,所以我无法检索正确的(硬编码)字符串类型,因此 AddVisitor 结构被使用。

我收到一个编译错误:no matching function for call to ‘get<0>(std::basic_string&)’

还有没有 operator+ 函数可以在没有 if-else 语句的情况下检测类型的方法?

我已经在 SO 中检查了很多答案,包括回答有关类似 Boost 功能的问题的答案,但我无法让它工作。

#include <iostream>
#include <variant>
#include <string>
#include "stdafx.h"


using Variant = std::variant<int, std::string>;

template<typename T>
struct AddVisitor
{
    T operator()(T v1, T v2)
    {
        return v1 + v2;
    }
};

class Var
{
    Variant v;

public:

    template<typename T>
    Var(T value) : v(value) {}

    Var operator+(Var& val)
    {
        // PROBLEM: This is a hard coded example that I want to use, so that concatenation of two strings happens.
        return std::visit(AddVisitor<std::string>(), std::get<std::string>(v), std::get<std::string>(val.get()));
       // Is there a way to get the correct type without if-else statements here?
    }

   Variant get()
   {
     return v;
   }
};

int main()
{
    Var x("Hello "), y("World");

    // The expected output is this:
    Var res = x + y;

    return 0;
}

我希望能够使用加号运算符连接两个字符串或两个整数并创建一个新的 Var 变量。

好的,有几件事要谈。

首先,具有多个变体参数的 std::visit 的访问者应该接受变体类型的所有组合。在你的情况下,它应该接受:

  • (string, string)
  • (string, int)
  • (int, int)
  • (int, string)

如果只有 string, stringint, int 有效,您仍然需要接受其他组合才能编译代码,但您可以将它们放入。

接下来,访问者不应被模板化。相反,operator() 应该为上述所有组合进行模板化或重载。

所以这里是 AddVisitor:

struct AddVisitor
{
    auto operator()(const std::string& a, const std::string& b) const -> Variant
    {
        return a + b;
    }
    auto operator()(int a, int b) const -> Variant
    {
        return a + b;
    }

    // all other overloads invalid
    template <class T, class U>
    auto operator()(T, U) const -> Variant
    {
        throw std::invalid_argument{"invalid"};
    }
};

从文档中不清楚重载可以做什么 return,但除非全部 return Variant,否则我无法编译它。幸运的是,编译器错误是 TREMENDOUSLY HELPFULL 。 (我需要检查标准)。

接下来,当您调用 std::visit 时,您需要传递您拥有的 variant

所以最后的代码是这样的:

auto operator+(Var& val) -> Var
{
    return std::visit(AddVisitor{}, get(), val.get());
}

而且您确实可以随心所欲地使用它:

Var res = x + y;

您的代码的另一个问题是 get 进行了不必要的复制。 std::variant 的副本制作起来并不便宜。所以我建议:

auto get() const -> const Variant&  { return v; }
auto get() -> Variant&  { return v; }