87

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

4

1 に答える 1

178

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

まず、デフォルトの文字特性クラスである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スタイルの文字列から構成する場合、文字列の新しい長さは、その文字列を呼び出すことによって導出されtraits::lengthます。これはtraits::length主に、C ++の文字列の「最小公分母」であるCスタイルの文字シーケンスを処理するためにstd::string使用されますが、任意の内容の文字列を処理するために使用されるようです。

于 2011-03-16T00:59:07.693 に答える