Используйте std :: option в качестве члена класса и примените посетителя - PullRequest
1 голос
/ 06 апреля 2019

Я пытаюсь использовать std :: variable в качестве переменной члена класса, а затем использовать перегрузку операторов, чтобы два 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.

1 Ответ

3 голосов
/ 06 апреля 2019

Хорошо, так что есть о чем поговорить.

Во-первых, посетитель для std::visit с более чем одним аргументом варианта должен принять все комбинации вариантов типов. В вашем случае он должен принять:

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

Если для вас действительны только string, string и int, 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, вам нужно передать variant s, которые у вас есть.

Итак, окончательный код такой:

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; }
...