我尝试将带有枚举的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;
}
答案 0 :(得分:3)
让我们从一个更简单的示例开始:
enum myEnum
{
INT8,
INT32
};
int foo1(myEnum bar1)
{
return bar1;
}
myEnum foo2(int bar2)
{
return bar2;
}
如果尝试对此进行编译,则编译器将报告foo2()
而不是foo1()
的编译错误。这告诉您可以将枚举隐式转换为整数值,但是不能将整数值隐式转换为枚举值。在-std=c++17
模式下的gcc 8.2发出了非常清晰的错误消息:
从'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
即可。