1

使用枚举时,我通常有一些与之相关的辅助方法。对于 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&);

使用类似命名空间的作用域,我有哪些选择来获得类型安全?

4

3 回答 3

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

这几乎与您对任何其他用户定义类型所做的事情相同。在这里试试上面的代码。请注意,在示例中,您可以将任何相等的可比较类型“转换”为字符串。

于 2013-07-19T04:38:52.240 回答
1

使用类似命名空间的作用域,我有哪些选择来获得类型安全?

这是类型安全的:

enum class Colour { RED, BLUE, GREEN };
string colour_to_string(Colour);
Colour from_string(string const&);

您可以决定将所有内容都放在命名空间中。枚举类型的行为类似于任何其他用户定义的类型。然而,第一个方法不需要类型信息(它可以被称为enum_to_string),而第二个方法需要一个涉及枚举类型的名称、一个命名空间,或者是一个函数模板。这是因为您不能根据返回类型重载。因此,您可以将所有内容放在命名空间中,并在使用 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
}
于 2013-07-19T04:38:18.640 回答
1

如果您enum class按照建议的方式使用 C++11,包括命名空间,确实需要两个限定符来访问它们:Colour::Colour::RED,您可能会觉得这很烦人。

但是,我认为将from_stringandto_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)as well 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,但为其他类型添加它会很简单。

使用 this 是类型安全的,并且Color仅在必要时才需要显式指定(作为模板参数或范围限定符),而不需要重复:

int main()
{
  Color c = Color::red;

  std::cout << to_string(c) << std::endl;
  c = from_string<Color>("red");

  return 0;
}

(如果您害怕名称冲突,或者通常不想将任何东西放在全局范围内,您可以将to_stringandfrom_string放入命名空间convert并将它们用作convert::from_string<Color>("red")等。甚至,创建一个类模板convert以便您可以将其称为convert<Color>::from_string("red"),这会产生非常易于准备、直观的代码。)

于 2013-07-19T04:58:39.390 回答