6

是否可以使 std::string 始终包含小写字符串?这是我将如何使用它:

typedef std::basic_string<...> lowercase_string;

void myfunc()
{
  lowercase_string s = "Hello World"; // notice mixed case
  printf(s.c_str());                  // prints "hello world" in lowercase
  std::string s2 = s;
  printf(s2.c_str());                 // prints "hello world" in lowercase
}
4

4 回答 4

19

您可以编写自己的 char 特征并将其std::basic_string作为第二个模板参数传递。

这是一个最小的例子:

template<typename T>
struct lowercase_char_traits : std::char_traits<T>
{
    static T* copy(T* dest, const T* src, std::size_t count )
    {
         for(size_t i = 0 ; i < count ; ++i)
              dest[i] = std::tolower(src[i]);
         return dest;
    }
    static void assign(T & out, T in)
    {
       out = std::tolower(in);
    }
    //implement other overload of assign yourself

    //note that you may have to implement other functionality 
    //depending on your requirement
};

然后将 typedef 定义为:

typedef std::basic_string<char, lowercase_char_traits<char>> lowercase;

这是一个测试程序:

int main() 
{
    lowercase s1 = "Hello World";
    std::cout << s1.c_str() << std::endl;

    lowercase s2 = "HELLO WORLD";
    std::cout << std::boolalpha << (s1 == s2) << std::endl;

    lowercase s3 = "HELLO";
    s3 += " WorL";
    s3.append("D");
    std::cout << std::boolalpha << (s1 == s3) << std::endl;

    std::cout << s2.c_str() << std::endl;
    std::cout << s3.c_str() << std::endl;
}

输出:

hello world
true
true
hello world
hello world

酷,不是吗?


请注意,要拥有一个完全工作的小写字符串类,您可能还需要定义其他功能lowercase_char_traits,具体取决于您希望从此类中获得什么行为。

请查看 Herb Sutter 精彩文章了解详细信息和解释:

希望有帮助。

于 2013-01-31T12:15:10.293 回答
2

std::string本身没有。

有各种替代方案,或多或少优雅,或多或少利弊。让我试着比较一下

封装

可能是最干净的解决方案:创建一个包含 std::string 的类,该类可以采用执行转换的 ctor 和分配。

  • 问题:std::sting 有超过一百种方法:如果您希望您的类公开所有方法...准备编写所有软管函数只是为了调用包装的函数。这是一个干净的“生产力问题”,似乎没有 OOP 狂热者照顾......可能是由键入的字符支付...... :-)
  • 优点:运行时多态(std::string 不支持)不会意外工作,因此代码更安全。

“部分”换行

和以前一样,但只涉及一些重要的方法或需要一些显式编码。

一个典型的实现可以是:

可:

class llstring
{
public:
    //just esplicitate a default
    llstring() :m() {}

    //this wors for all the std::string contructors but the ones specifically defined here
    template<class T, class... TT>
    llstring(T&& t, TT&&... tt) :m(std::forward<T>(t), std::forward<TT>(tt)...)
    {}

    // copy and move defaulted: just call the memebr ones
    llstring(const llstring&)=default;
    llstring(llstring&&) =default;

    //impose conversion
    llstring(const std::string& s) :m(lowercase(s)) {}
    llstring(const char* s) :m(lowercase(s)) {}

    //assign and transfer defaulted
    llstring& operator=(const llstring&)=default;
    llstring& operator=(llstring&&)=default;

    //impose conversion
    llstring& operator=(const std::sting& s) { m = lowercase(s); return *this; }
    llstring& operator=(const char* s) { m = lowercase(s); return *this; }

    //gets the "value"
    const std::string& str() const { return m; } 

private:
    std::string m;
};

此类本身无法进行任何算法和操作,但可以std::string通过调用str(). 并且可以接受任何 std::string 结果,通过转换获得。

可能是重新编码和维护风险之间的良好折衷

继承

std::string 作为基础,而不是成员。代码与上面类似(您必须提供一种在构造或分配上进行转换的方法)

  • 优点:原始 std::string 的接口和行为是自动公开的,因此所有 std::string 方法都可以工作并且可以访问。

  • 中性:从 std::string 向前(通过设计)和向后(通过基本继承)转换都有效。这可能会导致某些可能无法通过 llstring 的操作出现歧义。本身不是问题,但您必须了解函数名称解析和绑定是如何完成的。语言是明确指定的,但它是任何普通程序员并不总是知道的语言之一。

  • 缺点: llstring 公开了一个关于 std::string 的多态行为,它不会在多态方面表现出 llstring 的行为(没有方法是虚拟的,包括析构函数),因此你绝不能在 std::string* 上调用 delete (它是未定义的行为,如果它指向一个'llstring`)。

考虑到llstring和 string 都是值类型,这通常不应该发生(30 年来我从未写过一个new std::stringor delete pstring)。但这无论如何都会抓住 OOP 狂热者的所有咆哮,他们假装经典的 OOP 规则也适用于 string-s,即使它们不是 OOP 对象。

但是还有另一个-恕我直言更微妙的风险:在 llstring 和 string 之间的复合表达式中,所有中间结果都将是 string。并且中间操作不会在两者之间转换。所有这些都是隐含的。同样,语言规范定义明确,但控制一切可能并不容易。对尚未分配的中间结果的搜索可能会失败......因为内部意外的大写字母。

反向转换

不完全是你问的,但是......可能会更好地解决问题。

而不是“在返回目的地时转换”,而是“在离开源时转换”:

编写一个包装器(如上面的“部分扭曲”),它不是字符串隐式转换并具有显式 str() 函数,而是从字符串显式构造(即使没有转换)并具有隐式转换字符串 ( operator std::string() { return lowercase(m); })

这与您所要求的相反。如果允许存在大写字符串的点的数量很少尊重程序中的总字符串(您可以假设始终为小写)并且如果您可以授予您可以实现的所有 std::string 操作,那将是很好的小写字符串值永远不会生成大写字符串。

编辑:char_traits 解决方案

在Nawaz 帖子之后添加:

该解决方案尝试通过使 char 遵守另一种语义来改变行为(而不是value )。

  • 优点:简单,不需要大包装。快速编码。
  • 缺点:可能不完全符合预期:由于 std::string 函数都是可访问的,并且由于复制可能不是更改字符串内容的唯一方法,因此(在任何情况下)您都不会被授予大写字符它。除非您可以授予该副本,否则该副本将是更改字符串值的唯一方法。

注意:就像 一样string,也char_traits没有虚拟析构函数,但是,与字符串不同,没有 OOP 狂热者通常会大喊从它的继承。如果被问到,很可能会说“char_traits 上不会有动态分配”。再见连贯性。

综上所述

没有“低成本”的“完美解决方案”。他们都在某个阶段不完美

于 2013-01-31T13:08:01.543 回答
2

您可以使用私有继承。这将使您免于编写一堆包装器方法。

class lower_case_string : private std::string
{
    // define constructors that do the conversion to lower case
    // ...

    // expose functionality from std::string
    using std::string::size;
    using std::string::length;
    using std::string::cbegin;
    // etc.

    // Make sure NOT to expose methods that allow modification as they
    // could violate your invariant that all characters are lower case.
    // E.g., don't expose std::string::begin, instead write your own.
};
于 2013-01-31T13:32:25.210 回答
0

您必须在类周围创建一个包装器(正如 Chad 在评论中提到的那样,更好地使用组合),在其构造函数中将每个字符转换为其较低版本。不幸std::string的是,不能开箱即用。或者,创建您自己的函数来打印字符串,或者在打印之前将字符串转换为小写。

于 2013-01-31T12:08:19.857 回答