如何在C ++ 11中实现类型化字符串?

时间:2014-11-01 08:32:31

标签: c++ c++11 syntactic-sugar

在我的项目中,在同一范围内有许多具有不同含义的字符串,例如:

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?..

5 个答案:

答案 0 :(得分:6)

您可能需要带有标记参数的模板:

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

struct FunctionName;
MyString<FunctionName> function_name;

答案 1 :(得分:2)

简单方法:

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

您也可以通过隐式转换为字符串或其他成员函数来改进这一点。

答案 2 :(得分:2)

您可以使用我应用于this answer中的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;
}

答案 3 :(得分:1)

您的目标设计是继承,如other answer here(*)中所示。但是你不应该继承std :: string。您可以找到许多关于它的讨论,例如:Inheriting and overriding functions of a std::string?

它为您留下了第一个想法,实际上是实现了构图的概念。

(*)我会在答案中评论而不是打开新答案,但我还不能发表评论。

答案 4 :(得分:0)

这听起来有点低音。您希望为不同对象执行不同操作的多个函数具有相同的名称。这很奇怪,因为典型的方法是对于执行不同操作的函数具有相同的名称。

没有通过&#34;错误的事情&#34;很大程度上取决于程序员,你应该记住你在做什么,以及做什么。并在编写代码时测试代码,并定期运行测试以避免回归。

另一种方法是拥有一组保存数据的类,并具有通用接口,例如:

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

现在你可以&#34;处理&#34;所有OptionBase类型的事物都以一致的方式,在每个事件上调用相同的处理函数。但我不确定你在寻找什么。