9

次のようなコードを記述できるパラメータクラスを設計しました。

//define parameter
typedef basic_config_param<std::string> name;

void test(config_param param) {

  if(param.has<name>()) { //by name
    cout << "Your name is: " << param.get<name>() << endl;
  }

  unsigned long & n = param<ref<unsigned long> >(); //by type
  if(param.get<value<bool> >(true)) { //return true if not found
    ++n;
  }
}


unsigned long num = 0;
test(( name("Special :-)"), ref<unsigned long>(num) )); //easy to add a number parameter
cout << "Number is: " << num; //prints 1

クラスのパフォーマンスは非常に高速です。すべてがスタック上の単なる参照です。そして、すべての情報を保存するために、ヒープ割り当てに進む前に最大5つの引数の内部バッファーを使用して、すべてのオブジェクトのサイズを減らしますが、これは簡単に変更できます。

operator,()名前付きパラメーターを実装するためにオーバーロードして、この構文がより頻繁に使用されないのはなぜですか?パフォーマンスが低下する可能性があるためですか?

もう1つの方法は、名前付きイディオムを使用することです。

object.name("my name").ref(num); //every object method returns a reference to itself, allow object chaining.

しかし、私にとっては、operator,()二重括弧を使用することを忘れない限り、オーバーロードははるかに「現代的な」C++に見えます。通常の機能より遅くても性能はそれほど低下しないので、ほとんどの場合無視できます。

私はおそらくこのような解決策を思いついた最初の人ではありませんが、なぜそれがより一般的ではないのですか?上記の構文(私の例)のようなものは、それを受け入れるクラスを作成する前に見たことがありませんが、私にとっては完璧に見えます。

4

2 に答える 2

59

私の質問は、なぜこの構文がこれ以上使用されないのか、名前付きパラメーターを実装するために演算子()をオーバーロードするのかということです。

それは直感に反し、人間が読めない、そして間違いなく悪いプログラミング慣行だからです。コードベースを妨害したい場合を除いて、そうすることは避けてください。

test(( name("Special :-)"), ref<unsigned long>(num) ));

このコードフラグメントを初めて見たとしましょう。私の思考プロセスは次のようになります。

  1. 二重括弧を使用しているため、一見すると「最も厄介な解析」の例のように見えます。したがって、testは変数であると想定し、変数の型を書くのを忘れたかどうか疑問に思う必要があります。それから私はこのことが実際にコンパイルされることに気づきます。その後、これがすぐに破棄されたタイプテストのクラスのインスタンスであり、すべてのクラスタイプに小文字の名前を使用しているかどうか疑問に思う必要があります。
  2. それから私はそれが実際には関数呼び出しであることを発見します。素晴らしい。
  3. コードフラグメントは、2つの引数を持つ関数呼び出しのようになります。
  4. 二重括弧を使用したため、これは2つの引数を持つ関数呼び出しではないことがわかりました。
  5. だから、今私は一体何が起こっているのかを理解する必要があります()
  6. 前の引数を破棄するコンマ演算子(過去5年間、実際のC ++コードでは見たことがない)があることを覚えています。だから今、私はname()のその有用な副作用は何ですか、そしてname()は何ですか-関数呼び出しまたはタイプ(クラス/関数を区別するために大文字/小文字を使用しないため(すなわちTestはクラスですが、関数です)、プレフィックスtestはありません)。C
  7. nameソースコードを調べてみると、それがクラスであることがわかりました。また、演算子がオーバーロードされる,ため、実際には最初の引数が破棄されなくなります。

ここでどれだけの時間が無駄になっているのかわかりますか?率直に言って、そのようなものを書くと問題が発生する可能性があります。言語機能を使用して、コードを実際のコードとは異なるもののように見せるためです(1つの引数を使用して関数呼び出しを行うと、2つの引数があるように見えます。可変個引数関数です)。これは、加算の代わりに減算を実行するためにoperator+をオーバーロードすることとほぼ同等の悪いプログラミング手法です。

それでは、QStringの例を考えてみましょう。

 QString status = QString("Processing file %1 of %2: %3").arg(i).arg(total).arg(fileName);

人生で初めて見たとしましょう。それが私の思考プロセスの流れです。

  1. statusタイプQStringの変数があります。
  2. QString()型の一時変数から初期化されます。
  3. ... QString::argメソッドが呼び出された後。(私はそれが方法であることを知っています)。
  4. ドキュメントで.argを調べてその機能を確認すると、-style%1エントリが置き換えられ、QString&が返されることがわかります。したがって、一連の.arg()呼び出しは即座に意味をなします。QString :: argのようなものはテンプレート化でき、で引数のタイプを手動で指定しなくても、さまざまな引数タイプに対して呼び出すことができることに注意してください<>
  5. そのコードフラグメントは今では意味があるので、別のフラグメントに移ります。

非常に「モダン」なC++に見えます

「新しくて光沢がある」とは、「バギーで壊れている」ことを意味する場合があります(Slackware Linuxはやや似たアイデアに基づいて構築されています)。あなたのコードが現代的に見えるかどうかは関係ありません。人間が読める形式である必要があり、意図したとおりに機能する必要があります。また、書き込みに最小限の時間を浪費する必要があります。つまり、(個人的な推奨事項として)「最小のコストで最小の時間で最大の機能を実装する(メンテナンスを含む)」ことを目指す必要がありますが、それを実行すると最大の報酬を受け取ることができます。また、KISSの原則に従うことは理にかなっています。

「最新の」構文では、開発コストは削減されず、開発時間も短縮されず、保守コストも増加します(直感に反します)。結果として、この構文は避ける必要があります。

于 2012-04-06T01:18:04.220 に答える
3

必要はありません。動的ディスパッチ(引数の論理タイプに応じて動作が異なります)は、a)テンプレートの特殊化を使用して、a)はるかに簡単に、b)はるかに高速に実装できます。

そして、実行時にのみ利用可能な情報に基づいて実際に区別が必要な場合は、test関数をそのタイプの仮想メソッドに移動し、param動的バインディングを使用するようにします(それが目的であり、それがあなたの目的です)一種の再発明)。

このアプローチがより役立つ唯一のケースは、コードを削減し、いくつかの類似パターンを見つけることができる多重ディスパッチシナリオである可能性があります。

于 2012-04-06T00:12:06.980 に答える