使用 std::variant 作为类成员并应用访问者

Use std::variant as class member and apply visitor

本文关键字:成员 应用 访问者 std variant 使用      更新时间:2023-10-16

我正在尝试使用 std::variant 作为类成员变量,然后使用运算符重载,以便此类的两个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;
}

我希望能够使用 plus 运算符并连接两个字符串或两个整数并创建一个新的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"};
}
};

从文档中不清楚重载可以返回什么,但是除非所有返回都Variant,否则我无法对其进行编译。幸运的是,编译器错误非常有帮助。(我需要检查标准)。

接下来,当您调用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; }