как вернуть определенный тип из варианта с помощью посетителя? - PullRequest
0 голосов
/ 05 ноября 2018

У меня есть код ниже, и почему visitor1 и visitor2 выдает ошибки? Означает ли это, что посетитель не может вернуть один тип в варианте?

#include <iostream>
#include <variant>


struct Visitor1
{
    template <class T>
    T operator()(const T & t) const
    {
        return (t);
    }
};

struct Visitor2
{
    int operator()(const int  & t) const
    {
        return std::get<int>(t);
    }

    char operator()(const char & t) const
    {
        return std::get<char>(t);
    }
};

struct Visitor3
{
    void operator()(const int & t) const
    {
        std::cout<<t;
    }
    void operator()(const char & t) const
    {
        std::cout<<t;
    }
};

int main()
{
    std::variant<int, char> v{char(100)};

    std::visit(Visitor3{}, v);

    auto t = std::visit(Visitor2{}, v);  //fails
    //auto t = std::visit(Visitor1{}, v); //fails
    std::cout << t;
}

Я знаю, что могу использовать std::get(), но проблема в том, что я могу использовать auto только с std::get(), если я делаю что-то, как показано ниже, x недоступен за пределами области действия if / else:

bool b;
Variant v;
if (b)
{
   auto x = std::get<int>(v);
}
else
{
   auto x = std::get<char>(v);
}
// I want to do something with x here out of if/else

Ответы [ 3 ]

0 голосов
/ 05 ноября 2018

Язык может существовать со многими функциями C ++, который делает то, что вы хотите.

Чтобы сделать то, что вы хотите, при вызове std::visit необходимо написать N различных реализаций остальной функции.

В каждой из этих N различных реализаций (в вашем случае 2) тип переменной будет разным.

C ++ не работает таким образом.

Единственная часть кода, которая «умножается» на вызов, - это посетитель.

int main()
{
  std::variant<int, char> v{char(100)};

  std::visit([&](auto && t){
    std::cout << t;
  }, v);
}

Я помещаю остаток тела функции в посетителя. Этот код создается один раз для каждого типа, который может храниться в посетителе.

Все, что возвращает после посещения, возвращается в тело "единственного экземпляра" вызывающей области.

По сути, [&](auto&& t) лямбды делают то, что вы, кажется, хотите.


Теперь мы можем сделать несколько трюков, чтобы немного изменить синтаксис.

Мой любимый это:

v->*visit*[&](auto&& val) {
  std::cout << val;
  return [val](auto&& x) { x << val; };
}->*visit*[&](auto&& outputter) {
  outputer(std::cout);
};

, где ->*visit* использует относительно нелепое количество метапрограммирования, чтобы разрешить

  1. Названы операторы, вызывающие посещение,

  2. Слияние возвращаемых значений посещений в варианте.

но ни один здравомыслящий человек не напишет этот код.

0 голосов
/ 05 ноября 2018

Вы можете сделать

bool b;
Variant v;
std_optional<char> x_char;
std_optional<int> x_int;
if (b)
{
   x_int = std::get<int>(v);
}
else
{
   x_char = std::get<char>(v);
}
0 голосов
/ 05 ноября 2018

У меня есть код ниже, и почему visitor1 и visitor2 выдают ошибки?

Поскольку C ++ является строго типизированным языком.

Когда вы пишете

auto t = std::visit(Visitor2{}, v);  //fails

компилятор должен решить время компиляции, какой тип t, поэтому должен решить, какой тип возвращает std::visit(Visitor2{}, v).

Если Visitor2 возвращает char, когда v содержит char или int, когда v содержит int, компилятор не может выбрать (время компиляции! ) тип, возвращаемый из std::visit() [существует также проблема (только Visitor2), что t внутри operator() является int или char, поэтому вы не можете применить std::get() к нему].

Та же проблема с Visitor1: шаблон operator() возвращает тип шаблона, поэтому int или char для std::variant<int, char>.

Visitor3 работает, потому что оба operator() возвращают void, поэтому компилятор может разрешить (время компиляции), что std::visit(Visitor3{}, v) возвращает (в некотором смысле) void.

Может быть, лучше объяснить в этой странице :

[std::visit()] Эффективно возвращает

std::invoke(std::forward<Visitor>(vis), std::get<is>(std::forward<Variants>(vars))...) 

, где is... равно vars.index().... Тип возвращаемого значения выводится из возвращенного выражения, как если бы оно было decltype.

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

...