如何在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?..

您可能想要一个带有标记参数的模板:

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类型的内容,对每个内容调用相同的处理函数。但我不确定这是你想要的。

最新更新