参照渡しでパラメーターを渡すときに、関数のパラメーターに既定値を与えることは可能ですか? C++で
たとえば、次のような関数を宣言しようとすると:
virtual const ULONG Write(ULONG &State = 0, bool sequence = true);
これを行うとエラーが発生します:
エラーC2440:「デフォルト引数」:「const int」から「unsigned long&」に変換できません
Const参照に対してはできますが、非const参照に対してはできません。これは、C++では一時(この場合はデフォルト値)を非const参照にバインドできないためです。
これを回避する方法の1つは、実際のインスタンスをデフォルトとして使用することです。
static int AVAL = 1;
void f( int & x = AVAL ) {
// stuff
}
int main() {
f(); // equivalent to f(AVAL);
}
しかし、これは非常に限られた実用的です。
それはすでにあなたの答えに対する直接のコメントの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つだけで、それはコンストラクターにあります。あるコンストラクターを別のコンストラクターから呼び出すことはできないため、この場合、この手法は機能しません。
オプションの引数を提供する古いCの方法はまだあります:存在しない場合はNULLにできるポインター:
void write( int *optional = 0 ) {
if (optional) *optional = 5;
}
この小さなテンプレートは次のことに役立ちます。
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);
参照渡しで引数を渡す理由は2つあります。(1)パフォーマンスのため(この場合、const参照で渡したい)と(2)関数内の引数の値を変更する必要があるためです。
最新のアーキテクチャで符号なしの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);
もちろん、何を書いているのか、どこに書いているのかよくわかりません。しかし、それはまた別の質問です。
定数リテラルを関数呼び出しのパラメーターとして使用できないのと同じ理由で、デフォルトのパラメーターに定数リテラルを使用することはできません。参照値にはアドレスが必要です。定数参照値は必要ありません(つまり、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ポインターを渡します。ヌルオブジェクトアプローチが好きです。それは、呼び出し先コードをひどく複雑にすることなく、呼び出し元の生活を楽にします。
いいえ、できません。
参照渡しは、関数がパラメーターの値を変更する可能性があることを意味します。パラメータが呼び出し元によって提供されておらず、デフォルトの定数に由来する場合、関数は何を変更することになっていますか?
Stateのconst修飾子で可能です:
virtual const ULONG Write(const ULONG &State = 0, bool sequence = true);
void f(const double& v = *(double*) NULL)
{
if (&v == NULL)
cout << "default" << endl;
else
cout << "other " << v << endl;
}
私はそうは思いませんし、その理由は、デフォルト値は定数に評価され、参照によって渡される値は、定数参照であると宣言しない限り、変更できる必要があるからです。
別の方法は次のとおりです。
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"
OOの場合...指定されたクラスが「デフォルト」を持っていると言うことは、このデフォルト(値)を適宜宣言する必要があることを意味し、それからデフォルトパラメータとして使用できます。
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() ) {
この場合、「Global default Pagination」は単一の「参照」値であるため、このソリューションは非常に適しています。また、「gobal-level」構成のように、実行時にデフォルト値を変更することができます。例:ユーザーページネーションナビゲーション設定など。
これには回避策があります。int&
のデフォルト値に関する次の例を参照してください。
class Helper
{
public:
int x;
operator int&() { return x; }
};
// How to use it:
void foo(int &x = Helper())
{
}
bool
、double
など、必要な任意の単純なデータ型に対して実行できます...
これにはかなり汚いトリックもあります:
virtual const ULONG Write(ULONG &&State = 0, bool sequence = true);
この場合、std::move
で呼び出す必要があります。
ULONG val = 0;
Write(std::move(val));
それはおもしろい回避策にすぎません、私は実際のコードで使用することは全くお勧めしません!
void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, bool revealExtent = false);