Как вернуть определенный тип из варианта с помощью посетителя?

У меня есть код ниже и почему 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 ответа

Решение

У меня есть код ниже, и почему 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,

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

Язык может существовать со многими функциями 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. Слияние возвращаемых значений посещений в варианте.

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

Ты можешь сделать

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);
}
Другие вопросы по тегам