Давайте начнем с гораздо более простого примера:
enum myEnum
{
INT8,
INT32
};
int foo1(myEnum bar1)
{
return bar1;
}
myEnum foo2(int bar2)
{
return bar2;
}
Если вы попытаетесь скомпилировать это, ваш компилятор сообщит об ошибке компиляции с foo2()
, но не foo1()
. Это говорит о том, что перечисление может быть неявно преобразовано в целочисленное значение, но интегральное значение не может быть неявно преобразовано в значение перечисления. gcc 8.2 в режиме -std=c++17
выдает довольно четкое сообщение об ошибке:
неверное преобразование из "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
, это работает.