Как использовать 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
, это работает.