7


1

std :: stringの拡張に関するC ++の第一人者の意見が必要です。

私はいつも、STLの文字列にもう少し機能を追加することを望んでいました。 STL型をサブクラス化することは不可です。これらのクラスの拡張の推奨される方法は、型を最初の引数として取る関数(メンバー関数ではない)を記述することです。

このソリューションに興奮したことはありません。 1つは、そのようなメソッドがすべてコードのどこにあるかは必ずしも明らかではありませんが、もう1つは、構文が好きではないということです。 使用したいです。 メソッドを呼び出すとき!

少し前に、私は次のことを思いつきました。

class StringBox
{
public:
   StringBox( std::string& storage ) :
       _storage( storage )
   {
   }

   // Methods I wish std::string had...
   void Format();
   void Split();
   double ToDouble();
   void Join(); // etc...

private:
  StringBox();

  std::string& _storage;
};
StringBoxは、構築のためにstd

stringへの参照を必要とすることに注意してください…​ これは、その使用に興味深い制限を課します(そして、それが文字列クラスの拡散問題に寄与しないことを意味することを望みます)…​ 私自身のコードでは、ほとんど常にstd :: stringを変更するために、メソッドのスタックで宣言するだけです。

使用例は次のようになります。

string OperateOnString( float num, string a, string b )
{
    string nameS;
    StringBox name( nameS );

    name.Format( "%f-%s-%s", num, a.c_str(), b.c_str() );

    return nameS;
}

私の質問は次のとおりです。StackOverflowコミュニティのC ++の第一人者は、このSTL拡張の方法についてどう考えていますか?

7 Answer


19


_ このソリューションに興奮したことはありません。 1つは、そのようなメソッドがすべてコードのどこにあるかは必ずしも明らかではありませんが、もう1つは、構文が好きではないということです。 使用したいです。 メソッドを呼び出すとき! _

そして、メソッドを呼び出すときに `$!---&`を使用したい! それを扱います。 C コードを作成する場合は、C の規則に従ってください。 そして、非常に重要なC ++の規則は、可能な場合は非メンバー関数を優先することです。

C ++の達人がこれを推奨する理由があります。

カプセル化、拡張性、再利用を改善します。 ( `std

sort`は、すべてのイテレータペアで機能します。_because_単一のイテレータまたはコンテナクラスのメンバーではありません。 そして、どのように `std :: string`を拡張しても、非メンバー関数に固執する限り、それを破ることはできません。 また、クラスのソースコードにアクセスできない場合、またはソースコードを変更することを許可されていない場合でも、非メンバー関数を定義することで拡張できます)

個人的に、私はあなたのコードにポイントを見ることができません。 これは、はるかに単純で、読みやすく、短くありませんか?

string OperateOnString( float num, string a, string b )
{
    string nameS;
    Format(nameS, "%f-%s-%s", num, a.c_str(), b.c_str() );
    return nameS;
}

// or even better, if `Format` is made to return the string it creates, instead of taking it as a parameter
string OperateOnString( float num, string a, string b )
{
    return Format("%f-%s-%s", num, a.c_str(), b.c_str() );
}

ローマにいるとき、ことわざにあるように、ローマ人としてしてください。 _特に特別なこと_ローマ人が行うべき正当な理由があるとき。 そして特に、あなたのやり方が実際に単一の利点を持っていない場合。 エラーを起こしやすく、コードを読んでいる人を混乱させ、非慣用的であり、同じことをするためのコード行が増えています。

「string」を拡張する非メンバー関数を見つけるのが難しいというあなたの問題については、それが懸念される場合は名前空間に配置してください。 それが彼らの目的です。 「名前空間StringUtil」などを作成して、そこに配置します。


15


私たちのほとんどの「達人」はおそらく名前空間に含まれる無料の関数の使用を好むように思われるので、あなたのソリューションは人気がないと言っても安全だと思います。 私はそれが持っている一つの利点を見ることができないと思います、そしてクラスが参照を含むという事実は、ぶら下がり参照になることへの招待です。


2


まだ投稿されていないものを少し追加します。 Boost String Algorithms libraryは、無料のテンプレート関数アプローチを採用しており、それらが提供する文字列アルゴリズムは見事に再利用可能です。文字列のように見えるもの:std

string、char *、std :: vector、iterator pairs …​ あなたはそれを名前! そして、それらはすべてboost :: algorithm名前空間にきちんと配置されます(私はしばしば文字列操作コードをより簡潔にするために `using namespace algo = boost :: algorithm`を使用します)。

そのため、文字列拡張機能に無料のテンプレート関数を使用することを検討し、それらを「ユニバーサル」にする方法についてのBoost String Algorithmsを見てください。

安全なprintfスタイルの書式設定については、http://www.boost.org/doc/libs/1_41_0/libs/format/index.html [Boost.Format]をご覧ください。 文字列とストリームに出力できます。

私もすべてをメンバー関数にしたかったのですが、今では光が見え始めています。 UMLとdoxygenは、C ++ API ==クラス階層という考えに洗脳されたため、常にクラス内に関数を配置するようにプレッシャーをかけています。


1


文字列のスコープが `StringBox`と同じでない場合、セグメンテーション違反を取得できます:

StringBox foo() {
  string s("abc");
  return StringBox(s);
}

少なくとも、代入演算子とコピーctorプライベートを宣言することにより、オブジェクトのコピーを防止します。

class StringBox {
  //...
  private:
    void operator=(const StringBox&);
    StringBox(const StringBox&);
};

編集:APIに関して、驚きを防ぐために、 `StringBox`に文字列のコピーを所有させます。 私はこれを行う2つの方法を考えることができます:

  1. 文字列を(参照ではなく)メンバーにコピーし、後で結果を取得します- コピーとしても

  2. 次のような参照カウントスマートポインターを使用して文字列にアクセスします

    余分なコピーを防ぐための `std

    tr1 :: shared_ptr`または` boost:shared_ptr`


1


緩い関数の問題は、それらが緩い関数であることです。

STL関数が存在することを知らなかったために、ほとんどの人が既にSTLによって提供された関数を作成したこと、またはそれがあなたが達成しようとしていたことを行うことができると、お金を賭けます。

特に新しいユーザーにとっては、かなり罰せられたデザインです。 (STLも新たに追加され、さらに問題が増えます。)

Google:C ++から文字列

言及する結果の数:std

to_string

特定の関数のSTLバージョンを見つけるのと同じように、古代のCメソッドや自家製のバージョンを見つける可能性があります。

メンバーメソッドを見つけるのに苦労する必要がなく、古い非推奨バージョンなどを見つけることを心配する必要がないので、メンバーメソッドが非常に好きです。 (つまり、string.SomeMethodは、使用するメソッドであることがほぼ保証されており、Googleに具体的なものを提供します。)

'' '' '

C#スタイルの拡張メソッドが適切なソリューションです。

  1. それらは緩やかな機能です。

  2. それらは、インテリセンスを介してメンバー関数として表示されます。

これにより、誰もが正確に自分のしたいことができるようになります。

言語の変更を要求するのではなく、IDE自体で実現できるようです。

基本的に、インタープリターが存在しないメンバーへの呼び出しをヒットすると、一致するルーズ関数のヘッダーを確認し、コンパイラーに渡す前に動的に修正できます。

同様のことが、インテリセンスデータを読み込むときに実行できます。

既存の関数でこれがどのように機能するかはわかりません。このような大規模な変更は軽視すべきではありませんが、新しい構文を使用する新しい関数では問題になりません。

    namespace StringExt
    {
        std::string MyFunc(this std::string source);
    }
これは単独で使用することも、std

stringのメンバーとして使用することもできます。IDEはすべての面倒な作業を処理できます。

もちろん、これにより、さまざまな方法で解決できるさまざまなヘッダーにメソッドが分散されるという問題が残ります。

  1. いくつかの種類の拡張ヘッダー:共通を含むことができるstring_ext メソッド

  2. うーん……

それは問題を引き起こすことなく解決するのが難しい問題です…​


0


文字列に作用するために利用可能なメソッドを拡張したい場合、標準の文字列をパラメータとして取る静的メソッドを持つクラスを作成することで拡張します。 そうすれば、人々はあなたのユーティリティを自由に使用できますが、関数のシグネチャを変更して新しいクラスを取得する必要はありません。

これはオブジェクト指向モデルを少し壊しますが、コードをはるかに堅牢にします-つまり 文字列クラスを変更しても、他のコードにはそれほど影響しません。

推奨されるガイドラインに従ってください。理由はそこにあります:)


0


最適な方法は、テンプレート化された無料の関数を使用することです。 次に良いのはプライベート継承 `struct extended_str:private string`です。これは、コンストラクタを使用することで、C ++ 0xでたやすくなります。 プライベート継承は、いくつかのアルゴリズムを追加するだけでは手間がかかり、リスクが高すぎます。 あなたがしていることは、何に対しても危険です。

コードの句読点の変更を実現するために、重要なデータ構造を導入しました。 `string`ごとにBoxを手動で作成および破棄する必要がありますが、メソッドをネイティブのものと区別する必要があります。 あなたはすぐにこの慣習にうんざりするでしょう。