Как использовать std :: visit с std :: вариантом, содержащим enum - PullRequest
0 голосов
/ 10 января 2019

Я пытаюсь использовать 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 Ответ

0 голосов
/ 10 января 2019

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

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 , это работает.

...