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

问题描述 投票:1回答:1

我正在尝试使用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变量。

c++ std variant
1个回答
3
投票

好的,所以有几件事要谈。

首先,具有多个变量参数的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。幸运的是,编译器错误是TREMENDOUSLY HELPFULL。 (我需要检查标准)。

接下来,当你打电话给std::visit时,你需要通过你拥有的variants。

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

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; }
© www.soinside.com 2019 - 2024. All rights reserved.