В моем проекте много строк с разными значениями в одной и той же области видимости, например:
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?..
Вы, вероятно, хотите шаблон с параметром тега:
template<class Tag>
struct MyString
{
std::string data;
};
struct FunctionName;
MyString<FunctionName> function_name;
Простой подход:
struct flag {
string value;
};
struct name {
string value;
};
Вы можете улучшить это с помощью неявных преобразований в строки или другие функции-члены.
Вы можете использовать аналогичную технику, как я применил к 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;
}
Дизайн, к которому вы стремитесь, является наследством, как в другой ответ здесь(*). Но вы не должны наследовать от std :: string. Вы можете найти много дискуссий по этому поводу, например: Наследование и переопределение функций std :: string?.
Это оставляет вас с вашей первой идеей, фактически реализуя концепцию композиции.
(*) Я бы прокомментировал этот ответ вместо открытия нового ответа, но пока не могу комментировать.
Все это звучит немного баса назад. Вы хотите иметь одно и то же имя для нескольких функций, которые делают разные вещи для разных объектов. Это довольно странно, потому что типичный подход состоит в том, чтобы иметь одно и то же имя для функций, которые делают разные вещи.
Не передавать «неправильные вещи» в значительной степени зависит от программиста, вы должны помнить, что вы делаете и для чего. И тестировать ваш код во время его написания, и иметь тесты, которые вы регулярно запускаете, чтобы избежать регрессий.
Другой подход состоит в том, чтобы иметь набор классов, которые содержат ваши данные и имеют общий интерфейс, такой как этот:
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
последовательно вводите вещи, вызывая одну и ту же функцию обработки для каждой из них. Но я не уверен, что это то, что вы искали.