如何将 std::visit 与包含枚举的 std::variant 一起使用
How to use std::visit with std::variant containing enum
我尝试使用带有枚举的 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;
}
让我们从一个更简单的例子开始:
enum myEnum
{
INT8,
INT32
};
int foo1(myEnum bar1)
{
return bar1;
}
myEnum foo2(int bar2)
{
return bar2;
}
如果您尝试编译它,您的编译器将报告编译错误 foo2()
,而不是 foo1()
。这告诉您枚举可以隐式转换为整数值,但整数值不能隐式转换为枚举值。 gcc 8.2,在 -std=c++17
模式下,发出一条非常明确的错误消息:
invalid conversion from ‘int’ to ‘myEnum’ [-fpermissive]
让我们就此打住,在您理解这一点之前不要继续进行下去。整数值不能隐式转换为枚举,但枚举可以转换为整数值。
现在,让我们弄清楚这里发生了什么:
myEnum e;
updateValue(e, INT32);
您的演绎指南将使用 myEnum
实例化 visitHelper
。实际上,您是在创建以下模板实例:
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
)失败,因为这种隐式转换不是允许。
在您的工作案例中,您的模板 class 使用 T=int
实例化,并且 ()
重载采用 int
参数,并且由于每种类型的实例变体中的可以隐式转换为有效的 int
。
我尝试使用带有枚举的 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;
}
让我们从一个更简单的例子开始:
enum myEnum
{
INT8,
INT32
};
int foo1(myEnum bar1)
{
return bar1;
}
myEnum foo2(int bar2)
{
return bar2;
}
如果您尝试编译它,您的编译器将报告编译错误 foo2()
,而不是 foo1()
。这告诉您枚举可以隐式转换为整数值,但整数值不能隐式转换为枚举值。 gcc 8.2,在 -std=c++17
模式下,发出一条非常明确的错误消息:
invalid conversion from ‘int’ to ‘myEnum’ [-fpermissive]
让我们就此打住,在您理解这一点之前不要继续进行下去。整数值不能隐式转换为枚举,但枚举可以转换为整数值。
现在,让我们弄清楚这里发生了什么:
myEnum e;
updateValue(e, INT32);
您的演绎指南将使用 myEnum
实例化 visitHelper
。实际上,您是在创建以下模板实例:
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
)失败,因为这种隐式转换不是允许。
在您的工作案例中,您的模板 class 使用 T=int
实例化,并且 ()
重载采用 int
参数,并且由于每种类型的实例变体中的可以隐式转换为有效的 int
。