Как использовать std::visit с std:: вариантом, содержащим enum

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

#include <variant>
#include <iostream>
enum myEnum
{
    INT8,
    INT32
};

using value_t = std::variant<unsigned char , int, myEnum>;

template<class T, typename U = void>
struct visitHelper;

template<class T>
struct visitHelper <T>
{
    T &v;
    visitHelper(T &v): v(v){}
    void operator()(const T v){ this->v = v; }
};

template <typename T> visitHelper(T &v) -> visitHelper<T>;

template<class T>
void updateValue(T &v, value_t value)
{
    std::visit(visitHelper(v), value);
}


int main()
{
    /* uncomment this block will cause an compiler error
    myEnum e;              
    updateValue(e, INT32);
    std::cout << e << std::endl;
    */
    int i;
    updateValue(i, 17);
    std::cout << i << std::endl;
}

Почему этот код не компилируется, если я раскомментирую блок?

* Первое редактирование *

Я изменил код, чтобы он выглядел так, и теперь он работает.

#include <variant>
#include <iostream>
enum myEnum
{
    INT8,
    INT32
};

using value_t = std::variant<unsigned char , int, myEnum>;

template<class T, typename U = void>
struct visitHelper;

template<class T>
struct visitHelper <T, std::enable_if_t< std::is_arithmetic_v< T > > >
{
    T &v;
    visitHelper(T &v): v(v){}
    void operator()(const T v){ this->v = v; }
};
template<class T>
struct visitHelper <T, std::enable_if_t< std::is_enum_v< T > > >
{
    T &v;
    visitHelper(T &v): v(v){}
    void operator()(const T v){ this->v = v; }
    void operator()(const int v){ this->v = static_cast<T>(v); }
    void operator()(...){  }
};

template <typename T> visitHelper(T &v) -> visitHelper<T>;

template<class T>
void updateValue(T &v, value_t value)
{
    std::visit(visitHelper(v), value);
}


int main()
{
    myEnum e;
    updateValue(e, INT32);
    std::cout << e << std::endl;
    int i;
    updateValue(i, 18);
    std::cout << i << std::endl;
}

1 ответ

Решение

Давайте начнем с гораздо более простого примера:

enum myEnum
{
    INT8,
    INT32
};

int foo1(myEnum bar1)
{
    return bar1;
}

myEnum foo2(int bar2)
{
    return bar2;
}

Если вы попытаетесь скомпилировать это, ваш компилятор сообщит об ошибке компиляции с foo2(), но нет foo1(), Это говорит о том, что перечисление может быть неявно преобразовано в целочисленное значение, но интегральное значение не может быть неявно преобразовано в значение перечисления. ГЦК 8,2, в -std=c++17 mode выдает довольно четкое сообщение об ошибке:

недопустимое преобразование из int в myEnum [-fpermissive]

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

Теперь давайте разберемся, что здесь происходит:

myEnum e;              
updateValue(e, INT32);

Ваш гид вывода будет создан visitHelper с помощью myEnum, По сути, вы создаете следующий экземпляр шаблона:

struct visitHelper<myEnum>
{
    myEnum &v;
    visitHelper(myEnum &v): v(v){}
    void operator()(const myEnum v){ this->v = v; }
};

Вы передаете экземпляр этого объекта std::visitи то, что посещается, является экземпляром:

std::variant<unsigned char , int, myEnum>;

Требование к посетителю (несколько обобщенное) заключается в том, что он должен обеспечивать () перегрузка для каждого типа, сохраненного в посещаемом варианте.

Это () оператор должен принять unsigned char, int, а также myEnum ценности. Но единственное () Оператор у вашего посетителя занимает myEnum параметр, и, следовательно, попытка передать int (или unsigned char) не удается, потому что это неявное преобразование не допускается.

В вашем рабочем случае ваш шаблонный экземпляр создается с T=intи () перегрузка занимает int параметр, и так как экземпляр каждого типа в варианте может быть неявно преобразован в int, это работает.

Другие вопросы по тегам