8

このために尋ねられたc++のデフォルトの引数

次のような関数があるとします。void f(int p1=1, int p2=2, int p3=3, int p4=4);

そして、いくつかの引数のみを使用して呼び出したいと思います。残りはデフォルトになります。

このようなものが機能します:

template<bool P1=true, bool P2=true, bool P3=true, bool P4=true>
void f(int p1=1, int p2=2, int p3=3, int p4=4);
// specialize:
template<>
void f<false, true, false, false>(int p1) {
  f(1, p1);
}
template<>
void f<false, true, true, false>(int p1, int p2) {
  f(1, p1, p2);
}
// ... and so on. 
// Would need a specialization for each combination of arguments
// which is very tedious and error-prone

// Use:
f<false, true, false, false>(5); // passes 5 as p2 argument

しかし、実用的であるためにはあまりにも多くのコードが必要です。

これを行うためのより良い方法はありますか?

4

4 に答える 4

11

名前付きパラメータイディオムを使用します(→ FAQリンク)。

Boost.Parametersライブラリ(→ link)もこのタスクを解決できますが、コードの冗長性と明確さの大幅な低下によって代償が払われます。また、コンストラクターの処理も不十分です。もちろん、Boostライブラリをインストールする必要があります。

于 2011-11-18T07:15:55.207 に答える
7

Boost.Parameterライブラリをご覧ください。

名前付きパラメーターをC++で実装します。例:

#include <boost/parameter/name.hpp>
#include <boost/parameter/preprocessor.hpp>
#include <iostream>

//Define
BOOST_PARAMETER_NAME(p1)    
BOOST_PARAMETER_NAME(p2)
BOOST_PARAMETER_NAME(p3)
BOOST_PARAMETER_NAME(p4)

BOOST_PARAMETER_FUNCTION(
                         (void),
                         f,
                         tag,
                         (optional            
                          (p1, *, 1)
                          (p2, *, 2)
                          (p3, *, 3)
                          (p4, *, 4)))
{
    std::cout << "p1: " << p1 
            << ", p2: " << p2
            << ", p3: " << p3
            << ", p4: " << p4 << "\n";
}
//Use
int main()
{
    //Prints "p1: 1, p2: 5, p3: 3, p4: 4"
    f(_p2=5);
}
于 2011-11-18T06:13:56.257 に答える
4

Boost.Parametersはおもしろいですが、(残念ながら)多くの問題が発生します。その中には、プレースホルダーの衝突(および奇妙なプリプロセッサー/テンプレートエラーのデバッグが必要)があります。

BOOST_PARAMETER_NAME(p1)

_p1後で使用するプレースホルダーを作成します。同じプレースホルダーを宣言する2つの異なるヘッダーがある場合、競合が発生します。楽しくない。

Builderパターンに基づくと、はるかに単純な(概念的にも実際的にも)答えがあります。これは、名前付きパラメーターのイディオムです。

そのような関数を指定する代わりに:

void f(int a, int b, int c = 10, int d = 20);

operator():をオーバーライドする構造を指定します。

  • コンストラクターは必須の引数を要求するために使用され(厳密には名前付きパラメーターのイディオムではありませんが、盲目的に従わなければならないとは誰も言いませんでした)、オプションの引数にはデフォルト値が設定されています
  • 各オプションパラメータにはセッターが与えられます

一般に、これは、呼び出しを1行にチェーンできるように、セッターが現在のオブジェクトへの参照を返すようにするチェーンと組み合わされます。

class f {
public:
  // Take mandatory arguments, set default values
  f(int a, int b): _a(a), _b(b), _c(10), _d(20) {}

  // Define setters for optional arguments
  // Remember the Chaining idiom
  f& c(int v) { _c = v; return *this; }
  f& d(int v) { _d = v; return *this; }

  // Finally define the invocation function
  void operator()() const;

private:
  int _a;
  int _b;
  int _c;
  int _d;
}; // class f

呼び出しは次のとおりです。

f(/*a=*/1, /*b=*/2).c(3)(); // the last () being to actually invoke the function

必須の引数をパラメーターとして配置するバリアントを見てきましたoperator()。これにより、引数を属性として保持することを回避できますが、構文は少し奇妙です。

f().c(3)(/*a=*/1, /*b=*/2);

コンパイラーがすべてのコンストラクターとセッターの呼び出しをインライン化すると(これがここで定義されますが、そうでoperator()はありません)、「通常の」関数呼び出しと比較して同様に効率的なコードが得られるはずです。

于 2011-11-18T07:39:05.013 に答える
2

これは実際には答えではありませんが...

DavidAbrahamsとAlekseyGurtovoyによるC++テンプレートメタプログラミング(2004年に公開されました!)では、著者はこれについて次のように語っています。

この本を書いている間、名前付き関数パラメーターのサポートに使用されるインターフェースを再考しました。少し実験して、オーバーロードされた代入演算子でキーワードオブジェクトを使用することにより、理想的な構文を提供できることを発見しました。

f(slew = .799, name = "z");

彼らは続けて言います:

ここでは、この名前付きパラメーターライブラリの実装の詳細については説明しません。非常に簡単なので、演習として自分で実装してみることをお勧めします。

これは、テンプレートメタプログラミングとBoost::MPLのコンテキストでした。彼らの「単純な」実装がデフォルトのパラメータでどのように機能するかはよくわかりませんが、透過的であると思います。

于 2011-11-18T07:23:28.723 に答える