Как реализовать типизированные строки в C ++ 11?

В моем проекте много строк с разными значениями в одной и той же области видимости, например:

std::string function_name = "name";
std::string hash = "0x123456";
std::string flag = "--configure";

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

void Process(const std::string& string_type1);
void Process(const std::string& string_type2);

Очевидно, я должен использовать разные типы:

void Process(const StringType1& string);
void Process(const StringType2& string);

Но как реализовать эти типы в элегантной манере? Все, что я могу прийти, это:

class StringType1 {
std::string str_;
public:
explicit StringType1(const std::string& str) : str_(str) {}
std::string& toString() { return str_; }
};

// Same thing with StringType2, etc.

Можете посоветовать более удобный способ?


Нет смысла переименовывать функции, поскольку главная цель — не ошибочно передать один тип строки вместо другого:

void ProcessType1(const std::string str);
void ProcessType2(const std::string str);

std::string str1, str2, str3;

// What should I pass where?..

6

Решение

Вы, вероятно, хотите шаблон с параметром тега:

template<class Tag>
struct MyString
{
std::string data;
};

struct FunctionName;
MyString<FunctionName> function_name;
6

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

Простой подход:

struct flag {
string value;
};
struct name {
string value;
};

Вы можете улучшить это с помощью неявных преобразований в строки или другие функции-члены.

2

Вы можете использовать аналогичную технику, как я применил к std::vector в этот ответ.

Вот как это будет выглядеть для std::string:

#include <iostream>
#include <string>

template<typename Tag, class T>
struct allocator_wrapper : T
{ using T::T; };

template< typename Tag,
typename CharT = char,
typename Traits = std::char_traits<CharT>,
typename Allocator = std::allocator<CharT> >
using MyString = std::basic_string<CharT,Traits,allocator_wrapper<Tag, Allocator>>;

class HashTag;
class FlagsTag;

using Hash = MyString<HashTag>;
using Flags = MyString<FlagsTag>;

void foo( Hash ) {}

int main()
{
Hash hash( "12345" );
Flags flags( "--foo" );

foo( hash ); // that's fine

// you can already use them like strings in *some* contexts
std::cout << hash << " - " << flags << std::endl;
std::cout << ( hash == "12345" ) << std::endl;

// but they are not compatible types:
// won't compile:
// foo( flags );
// std::cout << ( hash == flags ) << std::endl;
}
2

Дизайн, к которому вы стремитесь, является наследством, как в другой ответ здесь(*). Но вы не должны наследовать от std :: string. Вы можете найти много дискуссий по этому поводу, например: Наследование и переопределение функций std :: string?.

Это оставляет вас с вашей первой идеей, фактически реализуя концепцию композиции.

(*) Я бы прокомментировал этот ответ вместо открытия нового ответа, но пока не могу комментировать.

1

Все это звучит немного баса назад. Вы хотите иметь одно и то же имя для нескольких функций, которые делают разные вещи для разных объектов. Это довольно странно, потому что типичный подход состоит в том, чтобы иметь одно и то же имя для функций, которые делают разные вещи.

Не передавать «неправильные вещи» в значительной степени зависит от программиста, вы должны помнить, что вы делаете и для чего. И тестировать ваш код во время его написания, и иметь тесты, которые вы регулярно запускаете, чтобы избежать регрессий.

Другой подход состоит в том, чтобы иметь набор классов, которые содержат ваши данные и имеют общий интерфейс, такой как этот:

class OptionBase
{
public:
OptionBase(const std::string &s) : str(s) {}
virtual void Process() = 0;
virtual std::string Value() { return str; }
virtual ~OptionBase() {}
protected:
std::string str;
};

class FlagOption: public OptionBase
{
public:
FlagOption(const std::string& s) : OptionBase(s) {}
void Process() override { ... do stuff here ... }
};

class HashOption: public OptionBase
{
public:
HashOption(const std::string& s) : OptionBase(s) {}
void Process() override { ... do has stuff here ... }
};class FunctionName: public OptoonBase
{
... you get the idea ...
};

Теперь вы можете «обработать» все ваши OptionBase последовательно вводите вещи, вызывая одну и ту же функцию обработки для каждой из них. Но я не уверен, что это то, что вы искали.

0
По вопросам рекламы [email protected]