Тип безопасных перечислений и вспомогательных функций

При использовании перечислений у меня обычно есть несколько вспомогательных методов, связанных с ними. Для перечислений в стиле 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&);

Какие у меня есть варианты для обеспечения безопасности типов с областью, подобной пространству имен?

1

Решение

Это путь:

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

это почти то же самое, что вы сделали бы с любым другим определяемым пользователем типом. Попробуйте приведенный выше код Вот. Обратите внимание, что в этом примере вы можете «преобразовать» в строку любой равный сопоставимый тип.

3

Другие решения

Если вы используете 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"), что приводит к очень легко готовому, интуитивно понятному коду.)

1

Какие у меня есть варианты для обеспечения безопасности типов с областью, подобной пространству имен?

Это безопасный тип:

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

Вы можете решить поместить все в пространство имен. Тип enum ведет себя как любой другой определенный пользователем тип. Однако, в то время как первый метод не нуждается в информации о типе (он может быть вызван 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
}
1
По вопросам рекламы [email protected]