C ++での参照渡し時のパラメーターのデフォルト値


116

パラメータを参照渡しする際に、関数のパラメータにデフォルト値を与えることは可能ですか?C ++

たとえば、次のような関数を宣言しようとすると、

virtual const ULONG Write(ULONG &State = 0, bool sequence = true);

これを行うとエラーが発生します。

エラーC2440: 'デフォルト引数': 'const int'から 'unsigned long&'に変換できません 'const'以外の参照は非左辺値にバインドできません


96
幸いにも、Googleのスタイルガイドに縛られることはありません。

23
「これを行わないでください。Googleスタイルガイド(および他のガイド)は、参照による非constパスを禁止しています」スタイルガイドには多くの主観的な部分が含まれていることが知られていると思います。これはそのうちの1つに見えます。
ヨハネスシャウブ-litb

13
WxWidgets style guide says "don't use templates" and they have good reasons<-ねじstd :: vector、私は言う
ヨハネスシャウブ-litb 09年

7
@jeffamaphone:Googleスタイルガイドでも、ストリームを使用しないように言われています。それらも避けることをお勧めしますか?
rlbond 2009年

10
ハンマーはドライバーを置くための恐ろしい道具ですが、それは釘でかなり使用可能です。機能を誤用する可能性があるという事実は、起こりうる落とし穴について警告するだけで、その使用を禁止するものではありません。
デビッドロドリゲス-10

回答:


102

const参照に対してはできますが、非const参照に対してはできません。これは、C ++では一時(この場合はデフォルト値)を非const参照にバインドできないためです。

これを回避する1つの方法は、実際のインスタンスをデフォルトとして使用することです。

static int AVAL = 1;

void f( int & x = AVAL ) {
   // stuff
} 

int main() {
     f();       // equivalent to f(AVAL);
}

しかし、これは非常に限られた実用的な使用です。


constにした場合、関数に別のアドレスを渡すことができますか?または、Stateのアドレスは常に0なので、意味がありませんか?

参照を使用している場合は、アドレスを渡しません。

3
boost :: array to the rescue void f(int&x = boost :: array <int、1>()[0]){..} :)
Johannes Schaub-litb 09年

1
実際に渡されているものに対処しない場合?

2
@Sony参照。それをアドレスと考えるのは誤りです。アドレスが必要な場合は、ポインターを使用します。

32

それはあなたの答えへの直接のコメントの1つですでに述べられましたが、それを公式に述べるためだけです。あなたが使いたいのはオーバーロードです:

virtual const ULONG Write(ULONG &State, bool sequence);
inline const ULONG Write()
{
  ULONG state;
  bool sequence = true;
  Write (state, sequence);
}

関数のオーバーロードを使用すると、さらに利点があります。まず、必要な引数をデフォルトにすることができます。

class A {}; 
class B {}; 
class C {};

void foo (A const &, B const &, C const &);
void foo (B const &, C const &); // A defaulted
void foo (A const &, C const &); // B defaulted
void foo (C const &); // A & B defaulted etc...

派生クラスの仮想関数にデフォルトの引数を再定義することもできます。これにより、オーバーロードによって以下が回避されます。

class Base {
public:
  virtual void f1 (int i = 0);  // default '0'

  virtual void f2 (int);
  inline void f2 () {
    f2(0);                      // equivalent to default of '0'
  }
};

class Derived : public Base{
public:
  virtual void f1 (int i = 10);  // default '10'

  using Base::f2;
  virtual void f2 (int);
};

void bar ()
{
  Derived d;
  Base & b (d);
  d.f1 ();   // '10' used
  b.f1 ();   // '0' used

  d.f2 ();   // f1(int) called with '0' 
  b.f2 ();   // f1(int) called with '0
}

デフォルトを実際に使用する必要がある状況は1つだけであり、それはコンストラクターにあります。あるコンストラクターを別のコンストラクターから呼び出すことはできないため、この場合、この手法は機能しません。


19
一部の人々は、default-paramがオーバーロードの巨大な乗算よりも恐ろしくないと考えています。
Boy氏

2
多分最後に、あなたはメンターします: d.f2(); // f2(int) called with '0' b.f2(); // f2(int) called with '0'
ピエトロ

4
最後のステートメント:C ++ 11以降では、委任コンストラクターを使用して、あるコンストラクターを別のコンストラクターから呼び出し、コードの重複を回避できます。
Fred Schoen 2015年

25

オプションの引数を提供する古いCの方法があります。存在しない場合はNULLになる可能性があるポインターです。

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}

私はこの方法がとても好きで、とても短くて簡単です。普段は必要としない追加の情報や統計情報などを返したい場合は、超実用的です。
uLoop 2017

11

この小さなテンプレートが役立ちます:

template<typename T> class ByRef {
public:
    ByRef() {
    }

    ByRef(const T value) : mValue(value) {
    }

    operator T&() const {
        return((T&)mValue);
    }

private:
    T mValue;
};

その後、次のことができるようになります。

virtual const ULONG Write(ULONG &State = ByRef<ULONG>(0), bool sequence = true);

ByRef記憶の点で、ライブのインスタンス化はどこにありますか?(コンストラクタのように)スコープを離れると破棄される一時的なオブジェクトではありませんか?
Andrew Cheong 2014

1
@AndrewCheongその意図はすべてその場で構築され、ラインが完成したときに破棄されます。これは、参照が必要な場合でもデフォルトのパラメーターを提供できるように、呼び出しの期間中に参照を公開する手段です。このコードはアクティブなプロジェクトで使用され、期待どおりに機能します。
マイクウィアー

7

いいえ、それは不可能です。

参照渡しは、関数がパラメーターの値を変更する可能性があることを意味します。パラメータが呼び出し元によって提供されず、デフォルトの定数からのものである場合、関数は何を変更することになっていますか?


3
従来のFORTRANの方法は、値0を変更することでしたが、C ++ではそれは起こりません。
David Thornley、

7

引数を参照渡しする理由は2つあります。(1)パフォーマンスのため(この場合はconst参照で渡したい)と(2)関数内の引数の値を変更する機能が必要なためです。

現代のアーキテクチャでunsigned longを渡すと、速度が遅くなりすぎることを強く疑います。したがってState、メソッド内の値を変更するつもりであると想定しています。定数0は右辺値(エラーメッセージでは「非左辺値」)であり、変更できない(エラーメッセージでは)ため、定数を変更できないため、コンパイラーは不平を言っていconstます。

簡単に言うと、渡された引数を変更できるメソッドが必要ですが、デフォルトでは、変更できない引数を渡したいとします。

言い換えると、非const参照は実際の変数を参照する必要があります。関数シグネチャ(0)のデフォルト値は実際の変数ではありません。あなたは同じ問題に遭遇しています:

struct Foo {
    virtual ULONG Write(ULONG& State, bool sequence = true);
};

Foo f;
ULONG s = 5;
f.Write(s); // perfectly OK, because s is a real variable
f.Write(0); // compiler error, 0 is not a real variable
            // if the value of 0 were changed in the function,
            // I would have no way to refer to the new value

Stateメソッド内で実際に変更するつもりがない場合は、単純にに変更できますconst ULONG&。ただし、それによってパフォーマンスが大幅に向上するわけではないので、非参照に変更することをお勧めしますULONG。既にが返されていることに気づきましたがULONG、その値はState必要な変更後の値であるという卑劣な疑いがあります。その場合、メソッドを次のように宣言するだけです。

// returns value of State
virtual ULONG Write(ULONG State = 0, bool sequence = true);

もちろん、あなたが何を書いているのか、どこに書いているのかはよくわかりません。しかし、これはまた別の問題です。


6

関数呼び出しのパラメーターとして定数リテラルを使用できないのと同じ理由で、デフォルトのパラメーターに定数リテラルを使用することはできません。参照値にはアドレスが必要ですが、定数参照値には必要ありません(つまり、r値または定数リテラルにすることができます)。

int* foo (int& i )
{
   return &i;
}

foo(0); // compiler error.

const int* bar ( const int& i )
{
   return &i;
}

bar(0); // ok.

デフォルト値にアドレスがあり、問題ないことを確認してください。

int null_object = 0;

int Write(int &state = null_object, bool sequence = true)
{
   if( &state == &null_object )
   {
      // called with default paramter
      return sequence? 1: rand();
   }
   else
   {
      // called with user parameter
      state += sequence? 1: rand();
      return state;
   }
}

このパターンを数回使用しましたが、変数またはnullの可能性があるパラメーターがありました。通常の方法は、ユーザーにポインターを渡すことです。値を入力したくない場合は、NULLポインターを渡します。オブジェクトのアプローチをnullにするのが好きです。それは、呼び出し先のコードをひどく複雑にすることなく、呼び出し元の生活を容易にします。


私見、このスタイルはかなり「臭い」です。デフォルトの引数が本当に正当化されるのは、コンストラクタで使用されるときだけです。他のすべてのケースでは、関数のオーバーロードは、デフォルトに関連する他の問題なしに、まったく同じセマンティクスを提供します。
Richard Corden、

1

私はそうは思いません。理由は、デフォルト値は定数に評価され、参照によって渡される値は、定数参照であると宣言しない限り、変更できる必要があるためです。


2
デフォルト値は「定数として評価」されません。

1

別の方法は次のようになります:

virtual const ULONG Write(ULONG &State, bool sequence = true);

// wrapper
const ULONG Write(bool sequence = true)
{
   ULONG dummy;
   return Write(dummy, sequence);
}

その後、次の呼び出しが可能です。

ULONG State;
object->Write(State, false); // sequence is false, "returns" State
object->Write(State); // assumes sequence = true, "returns" State
object->Write(false); // sequence is false, no "return"
object->Write(); // assumes sequence = true, no "return"

1
void f(const double& v = *(double*) NULL)
{
  if (&v == NULL)
    cout << "default" << endl;
  else
    cout << "other " << v << endl;
}

できます。基本的には、NULL参照参照をチェックするために参照値にアクセスすることです(論理的にはNULL参照はなく、指定したものがNULLであるだけです)。さらに、「参照」で動作するライブラリを使用している場合は、通常、ライブラリ固有の参照変数に対して同じことを行う「isNull()」のようないくつかのAPIがあります。そのような場合には、これらのAPIを使用することをお勧めします。
Parasrish 2016年

1

OOの場合...与えられたクラスが「デフォルト」を持っていると言うことは、このデフォルト(値)がそれに応じて宣言されなければならないことを意味し、それからデフォルトパラメータとして使用できますex:

class Pagination {
public:
    int currentPage;
    //...
    Pagination() {
        currentPage = 1;
        //...
    }
    // your Default Pagination
    static Pagination& Default() {
        static Pagination pag;
        return pag;
    }
};

あなたの方法で...

 shared_ptr<vector<Auditoria> > 
 findByFilter(Auditoria& audit, Pagination& pagination = Pagination::Default() ) {

この場合、「グローバルデフォルトのページ区切り」は単一の「参照」値であるため、このソリューションは非常に適しています。また、「gobal-level」構成のように、実行時にデフォルト値を変更する機能もあります。たとえば、ユーザーのページネーションナビゲーション設定などです。


1

Stateのconst修飾子で可能です:

virtual const ULONG Write(const ULONG &State = 0, bool sequence = true);

longをconst refとして渡すことは無意味であり、とんでもないことであり、OPが望むものを達成しません。
ジムバルター2017年


0

これにはかなり汚いトリックもあります:

virtual const ULONG Write(ULONG &&State = 0, bool sequence = true);

この場合は、次のように呼び出す必要がありますstd::move

ULONG val = 0;
Write(std::move(val));

それはほんの一部の面白い回避策ですが、実際のコードで使用することはまったくお勧めしません!


0

これに対する回避策があります。次のデフォルト値の例を参照してくださいint&

class Helper
{
public:
    int x;
    operator int&() { return x; }
};

// How to use it:
void foo(int &x = Helper())
{

}

booldouble... など、必要な簡単なデータ型に対してそれを行うことができます。


0

2つのオーバーロード関数を定義します。

virtual const ULONG Write(ULONG &State, bool sequence = true);

virtual const ULONG Write(bool sequence = true)
{
    int State = 0;
    return Write(State, sequence);
}

-3

virtual const ULONG Write(ULONG&State = 0、bool sequence = true);

答えは非常に簡単で、私は説明があまり得意ではありませんが、この関数で変更される可能性のある非定数パラメーターにデフォルト値を渡したい場合は、次のように使用します。

virtual const ULONG Write(ULONG &State = *(ULONG*)0, bool sequence =
> true);

3
NULLポインターの逆参照は無効です。これは場合によっては機能しますが、違法です。詳細はこちらparashift.com/c++-faq-lite/references.html#faq-8.7
Spo1ler
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.