SGI STLリファレンスのコピーに、キャラクター特性に関するページがありますが、これらがどのように使用されているかわかりません。それらはstring.h関数を置き換えますか?それらはによって使用されていないようですstd::string
。たとえば、length()
onのメソッドstd::string
はCharacterTraitslength()
メソッドを使用していません。キャラクター特性が存在するのはなぜですか?実際に使用されたことはありますか?
回答:
文字特性は、ストリーム/文字列クラスが、格納されている文字のロジックをそれらの文字に対して実行する必要のある操作のロジックから分離できるようにするため、ストリームおよび文字列ライブラリの非常に重要なコンポーネントです。
まず、デフォルトの文字特性クラスであるchar_traits<T>
、がC ++標準で広く使用されています。たとえば、というクラスはありませんstd::string
。むしろ、std::basic_string
次のようなクラステンプレートがあります。
template <typename charT, typename traits = char_traits<charT> >
class basic_string;
次に、std::string
次のように定義されます。
typedef basic_string<char> string;
同様に、標準ストリームは次のように定義されます。
template <typename charT, typename traits = char_traits<charT> >
class basic_istream;
typedef basic_istream<char> istream;
では、なぜこれらのクラスはそのまま構造化されているのでしょうか。奇妙な特性クラスをテンプレート引数として使用する必要があるのはなぜですか?
その理由は、場合によってはstd::string
、のような文字列が必要になることがありますが、プロパティが少し異なるためです。この典型的な例の1つは、大文字と小文字を区別しない方法で文字列を格納する場合です。たとえば、次のような文字列を作成したい場合CaseInsensitiveString
があります。
CaseInsensitiveString c1 = "HI!", c2 = "hi!";
if (c1 == c2) { // Always true
cout << "Strings are equal." << endl;
}
つまり、大文字と小文字の区別だけが異なる2つの文字列を等しく比較する文字列を作成できます。
ここで、標準ライブラリの作成者が特性を使用せずに文字列を設計したとします。これは、標準ライブラリに、私の状況ではまったく役に立たない非常に強力な文字列クラスがあることを意味します。この文字列クラスのコードの多くを再利用することはできませんでした。比較は常に、私が望んでいた方法に対して機能するためです。ただし、特性を使用することで、実際には、駆動std::string
するコードを再利用して大文字と小文字を区別しない文字列を取得することができます。
C ++ ISO標準のコピーを取り出して、文字列の比較演算子がどのように機能するかの定義を見ると、それらがすべてcompare
関数の観点から定義されていることがわかります。この関数は、を呼び出すことによって定義されます
traits::compare(this->data(), str.data(), rlen)
ここstr
で、は比較してrlen
いる文字列であり、は2つの文字列の長さのうち小さい方です。これは、の定義がテンプレートパラメータとして指定された特性タイプによってエクスポートされた関数をcompare
直接使用することを意味するため、実際には非常に興味深いものcompare
です。したがって、新しいトレイトクラスを定義し、compare
大文字と小文字を区別せずに文字を比較するように定義すると、次のように動作する文字列クラスを構築できます。std::string
にが、大文字と小文字を区別せずに処理。
これが例です。から継承しstd::char_traits<char>
て、作成しないすべての関数のデフォルトの動作を取得します。
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;
}
};
(私も定義しeq
、lt
ここで、文字がそれぞれ等しいか小さいかを比較してから定義したことに注意してくださいcompare
、この関数の観点からしたことに)。
このトレイトクラスができたので、次のようにCaseInsensitiveString
簡単に定義できます。
typedef std::basic_string<char, CaseInsensitiveTraits> CaseInsensitiveString;
そして出来上がり!これで、すべてを大文字と小文字を区別せずに処理する文字列ができました。
もちろん、これ以外にも特性を使用する理由は他にもあります。たとえば、固定サイズの基になる文字タイプを使用する文字列を定義する場合はchar_traits
、そのタイプに特化して、そのタイプから文字列を作成できます。たとえば、Windows APIには、TCHAR
前処理中に設定したマクロに応じて、幅の狭い文字またはワイド文字のいずれかであるタイプがあります。次に、次のようにTCHAR
記述して、sから文字列を作成できます。
typedef basic_string<TCHAR> tstring;
そして今、あなたはの文字列を持っています TCHAR
sの。
これらすべての例で、そのタイプの文字列を取得するために、いくつかの特性クラスをいくつかのテンプレートタイプのパラメーターとして定義した(またはすでに存在するものを使用した)ことに注意してください。これの要点は、作成basic_string
者が特性の使用方法を指定するだけで、デフォルトではなく特性を魔法のように使用して、デフォルトの文字列タイプの一部ではないニュアンスや癖のある文字列を取得できることです。
お役に立てれば!
編集:@phoojiが指摘したように、この特性の概念はSTLによって使用されるだけでなく、C ++に固有のものでもありません。完全に恥知らずな自己宣伝として、しばらく前に、特性を使用して任意のタイプの文字列を格納し、クライアントが格納する必要のある比較タイプを使用する三分探索木(ここで説明する基数ツリーのタイプ)の実装を作成しました。これが実際に使用されている場所の例を見たい場合は、興味深い読み物かもしれません。
編集:をstd::string
使用しないというあなたの主張に応えtraits::length
て、それはいくつかの場所で使用することが判明しました。最も注目すべきstd::string
は、char*
Cスタイルの文字列からoutを作成する場合、文字列の新しい長さは、traits::length
その文字列を呼び出すことによって導出されます。これtraits::length
は主に、C ++の文字列の「最小公分母」であるCスタイルの文字シーケンスを処理するためにstd::string
使用されますが、任意の内容の文字列を処理するために使用されるようです。