6

std::string に常に小文字の文字列を保持させることは可能ですか? これが私がそれを使用する方法です:

typedef std::basic_string<...> lowercase_string;

void myfunc()
{
  lowercase_string s = "Hello World"; // notice mixed case
  printf(s.c_str());                  // prints "hello world" in lowercase
  std::string s2 = s;
  printf(s2.c_str());                 // prints "hello world" in lowercase
}
4

4 に答える 4

19

std::basic_string独自の char 特性を記述し、それを2 番目のテンプレート引数として渡すことができます。

最小限の例を次に示します。

template<typename T>
struct lowercase_char_traits : std::char_traits<T>
{
    static T* copy(T* dest, const T* src, std::size_t count )
    {
         for(size_t i = 0 ; i < count ; ++i)
              dest[i] = std::tolower(src[i]);
         return dest;
    }
    static void assign(T & out, T in)
    {
       out = std::tolower(in);
    }
    //implement other overload of assign yourself

    //note that you may have to implement other functionality 
    //depending on your requirement
};

次に、typedef を次のように定義します。

typedef std::basic_string<char, lowercase_char_traits<char>> lowercase;

そして、ここにテストプログラムがあります:

int main() 
{
    lowercase s1 = "Hello World";
    std::cout << s1.c_str() << std::endl;

    lowercase s2 = "HELLO WORLD";
    std::cout << std::boolalpha << (s1 == s2) << std::endl;

    lowercase s3 = "HELLO";
    s3 += " WorL";
    s3.append("D");
    std::cout << std::boolalpha << (s1 == s3) << std::endl;

    std::cout << s2.c_str() << std::endl;
    std::cout << s3.c_str() << std::endl;
}

出力:

hello world
true
true
hello world
hello world

かっこいいですね。


完全に機能する小文字の文字列クラスを作成するには、そのようなクラスに必要な動作に応じて、他の機能も定義する必要があることに注意してください。lowercase_char_traits

詳細と説明については、Herb Sutter の素晴らしい記事をご覧ください。

それが役立つことを願っています。

于 2013-01-31T12:15:10.293 に答える
2

プライベート継承を使用できます。これにより、多数のラッパー メソッドを作成する必要がなくなります。

class lower_case_string : private std::string
{
    // define constructors that do the conversion to lower case
    // ...

    // expose functionality from std::string
    using std::string::size;
    using std::string::length;
    using std::string::cbegin;
    // etc.

    // Make sure NOT to expose methods that allow modification as they
    // could violate your invariant that all characters are lower case.
    // E.g., don't expose std::string::begin, instead write your own.
};
于 2013-01-31T13:32:25.210 に答える
2

std::stringそれ自体はしません。

さまざまな選択肢があり、多かれ少なかれエレガントで、多かれ少なかれ長所と短所があります。それらを比較してみましょう

カプセル化によるラップ

おそらく最もクリーンな解決策: std::string を含み、変換を実行する ctor と代入を使用できるクラスを作成します。

  • 問題: std::sting には 100 を超えるメソッドがあります。クラスでそれらすべてを公開する場合は、ラップされたものを呼び出すためだけにすべてのホース関数を作成する準備をしてください。これはクリーンな「生産性の問題」であり、OOP 熱狂者は対処していないようです...入力された文字によって支払われている可能性があります... :-)
  • 利点: ランタイム ポリモーフィズム (std::string でサポートされていない) が誤って機能しないため、コードがより安全になります。

「部分」ラップ

前と同じですが、いくつかの重要なメソッドにのみ関連するか、明示的なコーディングが必要です。

典型的な実装は次のとおりです。

次のことができます。

class llstring
{
public:
    //just esplicitate a default
    llstring() :m() {}

    //this wors for all the std::string contructors but the ones specifically defined here
    template<class T, class... TT>
    llstring(T&& t, TT&&... tt) :m(std::forward<T>(t), std::forward<TT>(tt)...)
    {}

    // copy and move defaulted: just call the memebr ones
    llstring(const llstring&)=default;
    llstring(llstring&&) =default;

    //impose conversion
    llstring(const std::string& s) :m(lowercase(s)) {}
    llstring(const char* s) :m(lowercase(s)) {}

    //assign and transfer defaulted
    llstring& operator=(const llstring&)=default;
    llstring& operator=(llstring&&)=default;

    //impose conversion
    llstring& operator=(const std::sting& s) { m = lowercase(s); return *this; }
    llstring& operator=(const char* s) { m = lowercase(s); return *this; }

    //gets the "value"
    const std::string& str() const { return m; } 

private:
    std::string m;
};

このクラス自体は、アルゴリズムや操作を行うことはできませんがstd::stringstr(). また、変換によって取得される std::string の結果を受け入れることができます。

おそらく、再コーディングとメンテナンスのリスクの間の適切な妥協案です

継承する

メンバーではなく、ベースとして std::string 。コードは上記と同様です (コンストラクトまたは代入時に変換する方法を提供する必要があります)。

  • 利点: 元の std::string のインターフェースと動作が自動的に公開されるため、すべての std::string メソッドが機能し、アクセス可能になります。

  • ニュートラル: std::string からの前方 (設計による) および後方 (基本継承による) の両方の変換が機能します。これにより、llstring を通過しない特定の操作であいまいさが生じる可能性があります。それ自体は問題ではありませんが、関数名の解決とバインドがどのように行われるかについては十分に理解しておく必要があります。言語は明確に規定されていますが、平均的なプログラマーには必ずしも知られていない言語の側面の 1 つです。

  • 欠点: llstring は std::string に関して多態的に動作しない多態的な動作を公開します (デストラクタを含め、どのメソッドも仮想ではありません)。 'llstring` を指している場合)。

llstringと string の両方が値型であることを考えると、これは通常発生しないはずです (30 年間、私は単一のnew std::stringorを書いたことはありませんdelete pstring)。しかし、これはいずれにせよ、たとえ OOP オブジェクトでなくても、string-s にも適用する古典的な OOP ルールを装う OOP 狂信者のすべての暴言をキャッチします。

しかし、別の-IMHOより微妙な-リスクがあります。llstringとstringの間の複合式では、すべての中間結果がstringになります。また、中間操作は途中で変換されません。そして、それはすべて暗黙的です。繰り返しになりますが、言語仕様は明確に定義されていますが、すべてを制御するのは容易ではないかもしれません。まだ割り当てられていない中間結果の検索は失敗する可能性があります... 内部に予期しない大文字が含まれているためです。

逆変換

あなたが尋ねたものとは正確には異なりますが...問題をより適切に反転させている可能性があります。

「宛先に到達するときに変換する」の代わりに、「ソースを離れるときに変換する」:

文字列から暗黙的に変換を取得し、明示的な str() 関数を持つ代わりに、n 文字列から明示的な構造を取り (変換なしでも)、暗黙的な変換を持つラッパー (上記の「部分ワープ」のように) を作成します。文字列( operator std::string() { return lowercase(m); })

これは、あなたが求めたものとは逆に機能します。大文字の文字列の存在が許可されるポイントの数が、プログラム内の文字列全体をほとんど尊重しない場合 (常に小文字であると想定できます)、および間に実装できるすべての std::string 操作を許可できる場合は良いでしょう。小文字の文字列値が大文字の文字列を生成することはありません。

編集: char_traits ソリューション

Nawaz の投稿の後に追加:

解決策は、 char を別のセマンティクスに準拠させることにより、動作( valueではなく)を変更しようとします。

  • 利点: シンプルで、大きなラッパーを必要としません。コーディングが速い。
  • 欠点: 意図したとおりではない可能性があります: std::string 関数はすべてアクセス可能であり、コピーが文字列の内容を変更する唯一の方法ではない可能性があるため、(いずれにせよ) 大文字の文字が決して存在しないことは認められませんそれ。そのコピーが文字列値を変更する唯一の方法になることを許可できない限り。

注: と同様stringに、仮想デストラクタもchar_traitsありませんが、文字列とは異なり、OOP 熱狂者は通常、文字列からの継承について叫ぶことはありません。そして、尋ねられた場合、おそらく「char_traits に動的割り当てはありません」と言うでしょう。さようならコヒーレンス。

結論は

There is no "perfect solution" with "low cost". All of them are somehow inperfect at some stage

于 2013-01-31T13:08:01.543 に答える
0

コンストラクターで各文字を下位バージョンに変換するクラスの周りにラッパーを作成する必要があります(コメントでChadが述べたように、そのための構成を使用することをお勧めします)。残念ながらstd::string、すぐにそれを行うことはできません。または、独自の関数を作成して文字列を出力するか、文字列を小文字に変換してから出力します。

于 2013-01-31T12:08:19.857 に答える