使用枚举时,我通常会有一些与之相关的辅助方法。对于C风格的枚举,我通常会这样做:
namespace Colour {
enum Enum { RED, BLUE, GREEN };
string to_string(Enum);
Enum from_string(string const&);
}
C ++ 11枚举类强制您使用恼人的前缀:
enum class Colour { RED, BLUE, GREEN };
string colour_to_string(Enum);
Enum colour_to_string(string const&);
使用类似命名空间的范围,我有什么选择获得类型安全性?
答案 0 :(得分:3)
这是要走的路:
#include <string>
#include <iostream>
enum class Colors{ RED, BLUE, GREEN };
struct myRedStruct{
bool operator==(const Colors& c)const{
if(c == Colors::RED)
return true;
return false;
}
};
namespace ColorsUtils {
using namespace std;
template <typename ColorComparable>
string to_string(const ColorComparable& c){
if(c == Colors::RED)
return "red";
return "not red";
}
Colors from_string(string const&);
}
int main() {
Colors c = Colors::BLUE;
const auto& s = ColorsUtils::to_string(c);
std::cout << s << std::endl;
myRedStruct mrs;
const auto & s2 = ColorsUtils::to_string(mrs);
std::cout << s2 << std::endl;
}
与使用任何其他用户定义类型完全相同。试试上面的代码here。请注意,在示例中,您可以“转换”为字符串任何相同的可比类型。
答案 1 :(得分:1)
使用类似命名空间的范围,我有什么选择获得类型安全性?
这是类型安全的:
enum class Colour { RED, BLUE, GREEN };
string colour_to_string(Colour);
Colour from_string(string const&);
您可以决定将所有内容放在命名空间中。枚举类型的行为与任何其他用户定义的类型相同。但是,第一种方法不需要类型信息(可以称为enum_to_string
),而第二种方法需要涉及枚举类型,命名空间或函数模板的名称。这是因为您不能基于返回类型重载。因此,您可以将所有内容放在命名空间中,并在使用枚举到字符串方法时利用与参数相关的查找:
namespace colour
{
enum class Colour { RED, BLUE, GREEN };
string to_string(Colour);
Colour from_string(string const&);
}
int main()
{
using colour::Colour;
Colour c{Colour::RED};
string s = to_string(c); // ADL kicks in
Colour c2 = colour::from_string("RED"); // no ADL, must specify namespace
}
答案 2 :(得分:1)
如果您按照建议的方式使用C ++ 11 enum class
,包括命名空间,确实需要两个限定符来访问它们:Colour::Colour::RED
,您可能会觉得这很烦人。
但是,我认为将from_string
和to_string
函数放入命名空间是不合适的 - 无论是出于类型安全还是出于任何其他原因 -
to_string()
适用于多种类型,不仅适用于Colour
。实际上,从C ++ 11开始,甚至有std::to_string
,您可以将其应用于各种内置类型,以将它们转换为std::string
。简单地扩展这个概念以涵盖用户定义的类型是有意义的:
template <typename T>
std::string to_string(const T arg)
{ return std::to_string(arg); }
template <>
std::string to_string(const Color c)
{
switch (c)
{
case Color::red:
return "red";
case Color::green:
return "green";
case Color::blue:
default:
return "blue";
}
}
然后,您可以使用to_string(42)
以及to_string(Color::red)
,这是完全类型安全的(与任何函数重载/模板特化一样,类型安全)。
同样适用于from_string
:
template <typename T>
T from_string(const std::string &str);
template <>
Color from_string<Color>(const std::string &str)
{
if (str == "red")
return Color::red;
else if (str == "green")
return Color::green;
else if (str == "blue")
return Color::blue;
else
throw std::invalid_argument("Invalid color");
}
我只提供了Color
的实现,但将其添加到其他类型中会很简单。
使用它是类型安全的,只有在必要时才需要显式指定Color
(作为模板参数或范围限定符),而不重复:
int main()
{
Color c = Color::red;
std::cout << to_string(c) << std::endl;
c = from_string<Color>("red");
return 0;
}
(如果您害怕名称冲突,或者通常不想在全球范围内放置任何内容,可以将to_string
和from_string
放入名称空间convert
并使用将它们设置为convert::from_string<Color>("red")
等。或者甚至创建一个类模板convert
,以便您可以将其称为convert<Color>::from_string("red")
,从而生成非常易于使用的直观代码。)