STLangular色特征有​​什么意义?

我注意到,在我的SGI STL参考副本中,有一个关于angular色特征的页面,但我看不到这些是如何使用的? 他们是否replacestring.h函数? 它们似乎不被std::string ,例如std::stringlength()方法不使用Character Traits的length()方法。 为什么angular色特质存在,他们曾经在实践中使用?

字符特征是stream和string库的一个非常重要的组成部分,因为它允许stream/string类从应该在这些字符上执行操作的逻辑中分离字符被存储的逻辑

首先,C ++标准中广泛使用了默认的字符特征类char_traits<T> 。 例如,没有称为std::string类。 相反,有一个类模板std::basic_string ,看起来像这样:

 template <typename charT, typename traits = char_traits<charT> > class basic_string; 

然后, std::string被定义为

 typedef basic_string<char> string; 

同样,标准stream被定义为

 template <typename charT, typename traits = char_traits<charT> > class basic_istream; typedef basic_istream<char> istream; 

那么为什么这些课程结构如此呢? 为什么我们应该使用一个奇怪的特质类作为模板参数?

原因是,在某些情况下,我们可能希望有一个string,就像std::string ,但有一些稍微不同的属性。 一个典型的例子就是如果你想以忽略大小写的方式存储string。 例如,我可能想创build一个名为CaseInsensitiveString的string,以便我可以拥有

 CaseInsensitiveString c1 = "HI!", c2 = "hi!"; if (c1 == c2) { // Always true cout << "Strings are equal." << endl; } 

也就是说,我可以有一个string,两个string的区别仅在于区分大小写的情况下是相等的。

现在,假设标准库作者devise的string没有使用特征。 这意味着我会在标准库中拥有一个非常强大的string类,在我的情况下完全没用。 我无法重复使用这个string类的大部分代码,因为比较总是与我希望它们工作的方式一致。 但是通过使用traits,实际上可以重用驱动std::string的代码来获取不区分大小写的string。

如果你拿出一个C ++ ISO标准的副本,看看string比较运算符的定义,你会发现它们都是用compare函数来定义的。 这个函数又是通过调用来定义的

 traits::compare(this->data(), str.data(), rlen) 

其中str是你比较的string,而rlen是两个string长度中较小的一个。 这实际上很有意思,因为它意味着compare的定义直接使用了指定的特征types导出的compare函数作为模板参数! 因此,如果我们定义一个新的特征类,然后定义compare以便不区分大小写地比较字符,我们可以构build一个类似于std::string的string类,但不区分大小写。

这是一个例子。 我们从std::char_traits<char>inheritance,以获得所有我们不写的函数的默认行为:

 class CaseInsensitiveTraits: public std::char_traits<char> { public: static bool lt (char one, char two) { return std::tolower(one) < std::tolower(two); } static bool eq (char one, char two) { return std::tolower(one) == std::tolower(two); } static int compare (const char* one, const char* two, size_t length) { for (size_t i = 0; i < length; ++i) { if (lt(one[i], two[i])) return -1; if (lt(two[i], one[i])) return +1; } return 0; } }; 

(注意我还定义了eqlt这里,它们分别比较了字符的相等和小于,然后定义compare这个函数)。

现在我们有了这个traits类,我们可以定义CaseInsensitiveString

 typedef std::basic_string<char, CaseInsensitiveTraits> CaseInsensitiveString; 

瞧! 我们现在有一个string,不区分大小写地处理所有的事情!

当然,除此之外,还有其他一些原因使用特质。 例如,如果要定义一个使用某种固定大小的基础字符types的string,则可以专门化该types的char_traits ,然后从该types创buildstring。 例如,在Windows API中,根据您在预处理期间设置的macros, TCHARtypes是窄字符还是宽字符。 然后你可以通过写入来从TCHAR创buildstring

 typedef basic_string<TCHAR> tstring; 

现在你有一串TCHAR

在所有这些例子中,请注意,我们只是定义了一些traits类(或者使用了一个已经存在的类)作为某个模板types的参数,以获得该types的string。 这一点是basic_string作者只需要指定如何使用traits,我们可以神奇地使他们使用我们的特质,而不是默认的string有一些细微差别或怪癖不属于默认的stringtypes。

希望这可以帮助!

编辑 :作为@phooji指出,这个特性的概念不只是由STL使用,也不具体到C ++。 作为一个完全无耻的自我推销,我写了一个三元search树 ( 这里描述的一种基数树)的实现,它使用特征来存储任何types的string,并使用客户希望它们存储的任何比较types。 如果你想看一个在实践中使用的例子,这可能是一个有趣的阅读。

编辑 :在你的声明std::string不使用traits::length ,它certificate它在几个地方。 最值得注意的是,当你从一个char* C风格的string构造一个std::string时,string的新长度是通过调用该string上的traits::length派生的。 似乎traits::length主要用于处理C风格的字符序列,这是C ++中string的“最小公分母”,而std::string则用于处理任意内容的string。