首页 > 解决方案 > 如何将 std::visit 与包含枚举的 std::variant 一起使用

问题描述

我尝试使用带有枚举的 std::variant 作为可能类型的一部分。我有一个编译错误,我找不到原因。如果我使用任何其他类型而不是枚举,则代码有效。这是我的代码的一部分:

#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;
}

标签: c++c++17std-variant

解决方案


让我们从一个更简单的例子开始:

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 charintmyEnum值。但是()访问者中唯一的运算符需要一个myEnum参数,因此尝试传递 an int(或 an unsigned char)失败,因为不允许这种隐式转换。

在您的工作案例中,您的模板类使用 实例化T=int,并且()重载带有一个int参数,并且由于变体中每种类型的实例都可以隐式转换为int,因此有效。


推荐阅读