STLキャラクター特性のポイントは何ですか?


83

SGI STLリファレンスのコピーに、キャラクター特性に関するページがありますが、これらがどのように使用されているかわかりません。それらはstring.h関数を置き換えますか?それらはによって使用されていないようですstd::string。たとえば、length()onのメソッドstd::stringはCharacterTraitslength()メソッドを使用していません。キャラクター特性が存在するのはなぜですか?実際に使用されたことはありますか?

回答:


171

文字特性は、ストリーム/文字列クラスが、格納されている文字のロジックをそれらの文字に対して実行する必要のある操作のロジックから分離できるようにするため、ストリームおよび文字列ライブラリの非常に重要なコンポーネントです

まず、デフォルトの文字特性クラスである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;
    }
};

(私も定義しeqltここで、文字がそれぞれ等しいか小さいかを比較してから定義したことに注意してくださいcompare、この関数の観点からしたことに)。

このトレイトクラスができたので、次のようにCaseInsensitiveString簡単に定義できます。

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

そして出来上がり!これで、すべてを大文字と小文字を区別せずに処理する文字列ができました。

もちろん、これ以外にも特性を使用する理由は他にもあります。たとえば、固定サイズの基になる文字タイプを使用する文字列を定義する場合はchar_traits、そのタイプに特化して、そのタイプから文字列を作成できます。たとえば、Windows APIには、TCHAR前処理中に設定したマクロに応じて、幅の狭い文字またはワイド文字のいずれかであるタイプがあります。次に、次のようにTCHAR記述して、sから文字列を作成できます。

typedef basic_string<TCHAR> tstring;

そして今、あなたはの文字列を持っています TCHARsの。

これらすべての例で、そのタイプの文字列を取得するために、いくつかの特性クラスをいくつかのテンプレートタイプのパラメーターとして定義した(またはすでに存在するものを使用した)ことに注意してください。これの要点は、作成basic_string者が特性の使用方法を指定するだけで、デフォルトではなく特性を魔法のように使用して、デフォルトの文字列タイプの一部ではないニュアンスや癖のある文字列を取得できることです。

お役に立てれば!

編集:@phoojiが指摘したように、この特性の概念はSTLによって使用されるだけでなく、C ++に固有のものでもありません。完全に恥知らずな自己宣伝として、しばらく前に特性を使用して任意のタイプの文字列を格納し、クライアントが格納する必要のある比較タイプを使用する三分探索木ここで説明する基数ツリーのタイプ)の実装を作成しました。これが実際に使用されている場所の例を見たい場合は、興味深い読み物かもしれません。

編集:をstd::string使用しないというあなたの主張に応えtraits::lengthて、それはいくつかの場所で使用することが判明しました。最も注目すべきstd::stringは、char*Cスタイルの文字列からoutを作成する場合、文字列の新しい長さは、traits::lengthその文字列を呼び出すことによって導出されます。これtraits::lengthは主に、C ++の文字列の「最小公分母」であるCスタイルの文字シーケンスを処理するためにstd::string使用されますが、任意の内容の文字列を処理するために使用されるようです。



14
ユーザー名を正義にしたようです:)おそらく関連性もあります。Boostライブラリの多くは概念と型特性クラスを使用しているため、標準ライブラリだけではありません。さらに、同様の手法がテンプレートを使用せずに他の言語で使用されています。難解な例を参照してください:ocaml.janestreet.com/?q = node / 11
phooji 2011年

2
素晴らしい構造(三分探索木)ですが、試行はさまざまな方法で「圧縮」できることを指摘しておきます。1/単一の文字ではなく、文字の範囲を使用して子を指す(ゲインは明らかです)、2 /パス圧縮(パトリシアツリー)とブランチの終わりの3 /バケット(つまり、K未満である限り、文字列のソートされた配列を使用します)。これらを組み合わせると(私は1と3を組み合わせました)、速度パフォーマンスに一定の要因を超える影響を与えることなく、メモリ消費を大幅に削減します(実際、バケットはジャンプの数を減らします)。
Matthieu M.

2
@ dan04:関数を使用するための標準クラス/アルゴリズムを取得してみてください。
xeo 2012年

2
つまり...これを一言で言えば、トレイトは、basic_stringクラスが実際に何であるかに関係なく、さまざまなタイプの文字を操作するために使用する一種のインターフェースですよね?
virus721 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.