在我的项目中,枚举值通常需要写出日志文件或作为字符串保存。所以,我已经提供了ToString和StringToEnum函数,如下例所示:
namespace Mine
{
enum class Color { red, green, blue };
inline std::wstring ToString(Color c)
{
switch (c)
{
case Color::red: return L"red";
case Color::green: return L"green";
case Color::blue: return L"blue";
default: THROW_MACRO("Unexpected value[{}] for enum[{}]", c, L"Color");
}
}
inline void StringToEnum(const std::wstring& inEnumValueName, Color& out)
{
if (inEnumValueName == L"red")
{
out = Color::red;
}
else if (inEnumValueName == L"green")
{
out = Color::green;
}
else if (inEnumValueName == L"blue")
{
out = Color::blue;
}
else
{
THROW_MACRO("Unexpected value[{}] for enum[{}]", inEnumValueName, L"Color");
}
}
}
当我使用StringToEnum时,我最终写了:
Color c;
StrintToEnum(L"red", c);
// use c
我真的希望能够在一行上声明和初始化并写下:
auto c = ToEnum<Mine::Color>(L"red);
我像这样定义了ToEnum并将其放在要包含的标题中:
namespace CommonCode
{
template<class T>
T ToEnum(const std::wstring& enumValueName)
{
T value;
StringToEnum(enumValueName, value);
return value;
}
}
问题是,ToEnum无法编译,因为尚未定义相关的StringToEnum函数。
是否有一种有用的方法可以对此进行编码,或者每当我想要从字符串声明和初始化枚举值时,我是否需要编写两行代码?
我试图专门化ToEnum,但是我遇到了这个问题,我需要关闭命名空间我定义枚举,打开CommonCode命名空间并添加到那个,然后再回到原始命名空间。这是很多打字,看起来很难看。
(我使用的是Visual Studio 2015 Update 3,因此任何使用它编译的解决方案都是首选。)
答案 0 :(得分:1)
StringToEnum
实际上应该返回Enum
。原样,充其量使用有点令人讨厌。自MSVC incorrectly implements两阶段模板查找以来,您的函数模板解决方案失败。因此,我们只需将其他内容传递给我们可以用来确定类型的StringToEnum
。尽可能清楚地说明这个&#34;其他东西&#34;的目的是什么?是的,我们将它变成自己的类型:
template <class T> struct tag { };
在您的原始示例中:
inline Color StringToEnum(const std::wstring& inEnumValueName, tag<Color> )
{
if (inEnumValueName == L"red")
{
return Color::red;
}
else if (inEnumValueName == L"green")
{
return Color::green;
}
// etc.
}
现在,只要您想将字符串转换为枚举E
,那就是:
auto e = StringToEnum(str, tag<E>{});
可以通过泛型过载缩短:
template <class T>
inline T StringToEnum(const std::wstring& name) {
return StringToEnum(name, tag<T>{} );
}
auto e = StringToEnum<E>(str);
答案 1 :(得分:0)
您有不同的名称空间。你的模板声明几乎是正确的,但它需要知道什么是 StringToEnum
。您只需在使用函数时放置命名空间:
namespace CommonCode {
template<class T>
T ToEnum(const std::wstring& enumValueName) {
T value;
// Notice the namespace here
Mine::StringToEnum(enumValueName, value);
return value;
}
}
击> <击> 撞击> 我建议直接直接实例化每个函数:
template<class T>
T toEnum(const std::string&);
template<>
Color toEnum<Color>(const std::string&) {
if (inEnumValueName == L"red") {
out = Color::red;
}
// ...
}
它将删除样板文件,让您按照自己的意愿使用函数,并以正确的方式实现该语法。
答案 2 :(得分:0)
您可以使用模板专业化:
#include <string>
enum class Fail { err };
enum class Color { red,blue,green};
//generic template: causes error in compilation
template<typename T>
T enumToString(std::string s)
{
static_assert(sizeof(T) == -1,"no overload for enum");
}
//specialization for Color
template<>
Color enumToString(std::string s)
{
if(s == "red")
return Color::red;
else if(s == "blue")
return Color::blue;
return Color::green;
}
int main()
{
auto c = enumToString<Color>("blue");
// auto e = enumToString<Fail>("e"); //this will cause a compiler error
return static_cast<int>(c);
}
只需为您需要的每个枚举专门化模板。我还添加了一个默认实现,如果没有专门化,它将失败。