web-dev-qa-db-ja.com

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

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

79
Matthew Smith

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

まず、デフォルトの文字特性クラス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があります。次に、TCHARsから文字列を作成できます。

typedef basic_string<TCHAR> tstring;

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

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

お役に立てれば!

[〜#〜] edit [〜#〜]:@phoojiが指摘したように、この特性の概念はSTLによって使用されるだけでなく、 C++に固有です。完全に恥知らずな自己宣伝として、しばらく前に私は書きました 三分探索ツリーの実装 (基数ツリーのタイプ ここで説明 )は、特性を使用して任意のタイプと、クライアントが保存してほしい比較タイプを使用します。これが実際に使用されている場所の例を確認したい場合は、興味深い資料になるでしょう。

[〜#〜] edit [〜#〜]std::stringtraits::lengthを使用していないというあなたの主張に応えて、それはいくつかの場所で行うことがわかりました。特に、std::string Cスタイルの文字列からchar*を構築する場合、その文字列に対してtraits::lengthを呼び出すことにより、文字列の新しい長さが導出されます。 traits::lengthは、C++の文字列の「最小公分母」であるCスタイルの文字シーケンスを処理するために主に使用されているようですが、std::stringは任意の内容の文字列を処理するために使用されています。

157
templatetypedef