デフォルト引数
末尾の引数を1つ以上提供せずに関数を呼ぶことを可能とします。
関数宣言の parameter-list で引数に対して以下の構文を用いることによって指定されます。
attr(オプション) decl-specifier-seq declarator = initializer
|
(1) | ||||||||
attr(オプション) decl-specifier-seq abstract-declarator(オプション) = initializer
|
(2) | ||||||||
デフォルト引数は関数呼び出しで末尾の引数が欠けている場所で使用されます。
void point(int x = 3, int y = 4);
point(1,2); // point(1,2) を呼びます。
point(1); // point(1,4) を呼びます。
point(); // point(3,4) を呼びます。
関数宣言において、デフォルト引数付きの引数の後、すべての後続の引数は、以下を満たさなければなりません。
- この宣言または同じスコープの以前の宣言から供給されるデフォルト引数を持つ。
int x(int = 1, int); // エラー (x の以前の宣言はないと仮定しています)。
void f(int n, int k = 1);
void f(int n = 0, int k); // OK、 k のデフォルトは同じスコープの以前の宣言から供給されます。
void g(int, int = 7);
void h() {
void g(int = 1, int); // エラー、同じスコープではありません。
}
template<class ...T> struct C { void f(int n = 0, T...); };
C<int> c; // OK、宣言 void C::f(int n = 0, int) を実体化します。
template<class...T> void h(int i = 0, T... args); // OK。
|
(C++11以上) |
省略記号は引数ではなく、そのためデフォルト引数を持つ引数の後に置くことができます。
int g(int n = 0, ...); // OK。
デフォルト引数は関数宣言およびラムダ式 (C++14以上)の引数リストでのみ使用でき、関数へのポインタの宣言、関数への参照の宣言、または typedef 宣言では使用できません。 テンプレート引数リストはデフォルトテンプレート引数に対して同様の構文を使用します。
非テンプレート関数では、関数が同じスコープで再宣言された場合、すでに宣言された関数にデフォルト引数を追加できます。 関数呼び出しの地点においては、デフォルト引数はその関数に対するすべての可視な宣言で提供されるデフォルト引数の和です。 再宣言はデフォルトがすでに可視である引数に対してデフォルトを導入することはできません (たとえ値が同じであっても)。 内側のスコープ内の再宣言は外側のスコープからデフォルト引数を取得しません。
void f(int, int); // #1
void f(int, int = 7); // #2 OK、デフォルトを追加します。
void h() {
f(3); // #1 と #2 が可視です。 f(3,7) の呼び出しを行います。
void f(int = 1, int); // エラー、内側のスコープの宣言はデフォルトを取得しません。
}
void m() { // 新たなスコープが始まります。
void f(int, int); // 内側のスコープの宣言。 デフォルトを持ちません。
f(4); // エラー、 f(int, int) を呼ぶために十分な引数がありません。
void f(int, int = 6);
f(4); // OK、 f(4,6); を呼びます。
void f(int, int = 6); // エラー、同じスコープでデフォルトを再宣言できません。
}
void f(int = 1, int); // #3 OK、 #2 にデフォルトを追加します。
void n() { // 新たなスコープが始まります。
f(); // #1、 #2、および #3 が可視です。 f(1, 7); を呼びます。
}
異なる翻訳単位でインライン関数が宣言された場合、デフォルト引数の累積された集合は各翻訳単位の終わりで同じでなければなりません。
|
非インライン関数が異なる翻訳単位内の同じ名前空間スコープ内で宣言された場合、対応するデフォルト引数は、もし存在するならば、同じでなければなりません (一部の翻訳単位内で一部のデフォルト引数が欠けていても構いません)。 |
(C++20以上) |
フレンド宣言がデフォルトを指定する場合、それはフレンド関数の定義でなければならず、その翻訳単位内ではその関数の他の宣言は許されません。
using 宣言は既知のデフォルト引数の集合を持ち越します。 その関数の名前空間に後程さらなる引数が追加された場合、それらのデフォルトも using 宣言が可視なあらゆる場所で可視になります。
namespace N {
void f(int, int = 1);
}
using N::f;
void g() {
f(7); // f(7, 1); を呼びます。
f(); // エラー。
}
namespace N {
void f(int = 2, int);
}
void h() {
f(); // f(2, 1); を呼びます。
}
デフォルト引数で使用される名前は、宣言の時点で名前探索され、アクセス可能性がチェックされ、束縛されますが、関数呼び出しの時点で実行されます。
int a = 1;
int f(int);
int g(int x = f(a)); // f に対する名前探索は ::f を発見し、 a に対する名前探索は ::a を発見します。
// ::a の値 (この時点では 1) は使用されません。
void h()
{
a = 2; // ::a の値を変更します。
{
int a = 3;
g(); // f(2) を呼び、その戻り値を用いて g() を呼びます。
}
}
非テンプレートクラスのメンバ関数の場合、デフォルト引数はクラス外側の定義でも指定でき、クラス本体内側の宣言で提供されたデフォルト引数と組み合わされます。 これらのクラス外側のデフォルトがメンバ関数をデフォルト、コピー、またはムーブコンストラクタに転換させるであろう場合、プログラムは ill-formed です。 クラステンプレートのメンバ関数の場合、すべてのデフォルトはメンバ関数の最初の宣言で提供されなければなりません。
class C {
void f(int i = 3);
void g(int i, int j = 99);
C(int arg); // 非デフォルトコンストラクタ。
};
void C::f(int i = 3) { // エラー、デフォルト引数はクラススコープ内ですでに指定されています。
}
void C::g(int i = 88, int j) { // OK、この翻訳単位では C::g は引数なしで呼べます。
}
C::C(int arg = 1) { // エラー、このコンストラクタをデフォルトコンストラクタに転換させます。
}
仮想関数のオーバーライダーは基底クラスの宣言からデフォルト引数を取得せず、仮想関数の呼び出しが行われたとき、デフォルト引数はオブジェクトの静的な型に基づいて決定されます (ノート: これは非仮想インタフェースパターンを用いて回避できます)。
struct Base {
virtual void f(int a = 7);
};
struct Derived : Base {
void f(int a) override;
};
void m() {
Derived d;
Base& b = d;
b.f(); // OK、 Derived::f(7) を呼びます。
d.f(); // エラー、デフォルトはありません。
}
デフォルト引数ではローカル変数は使用できません (未初期化文脈内での使用は除きます) (C++14以上)。
void f()
{
int n = 1;
extern void g(int x = n); // エラー、ローカル変数はデフォルトにできません。
extern void h(int x = sizeof n); // OK (CWG 2082 以降の場合)。
}
デフォルト引数では this ポインタは使用できません。
class A {
void f(A* p = this) { } // エラー、 this は使用できません。
};
デフォルト引数では非静的クラスメンバは使用できません (たとえ評価されない場合でも)。 ただしメンバアクセス式でメンバへのポインタを形成するために使用されるときは除きます。
int b;
class X {
int a;
int mem1(int i = a); // エラー、非静的メンバは使用できません。
int mem2(int i = b); // OK、名前探索は静的メンバ X::b を発見します。
static int b;
};
デフォルト引数では関数の引数は使用できません (たとえ評価されない場合でも) (C++14未満) (未評価の場合は除きます) (C++14以上)。 引数リスト内で先に現れた引数はスコープ内であることに注意してください。
int a;
int f(int a, int b = a); // エラー、デフォルト引数で引数 a が使用されました。
int g(int a, int b = sizeof a); // エラー (CWG 2082 より前の場合)。
// OK (CWG 2082 以降の場合)。 未評価文脈における使用は OK です。
デフォルト引数は関数型の一部ではありません。
int f(int = 0);
void h() {
int j = f(1);
int k = f(); // f(0); を呼びます。
}
int (*p1)(int) = &f;
int (*p2)() = &f; // エラー、 f の型は int(int) です。
デフォルト引数にラムダ式が現れた場合、それは明示的にも暗黙的にも何もキャプチャできません。
void f2() {
int i = 1;
void g1(int = ([i]{ return i; })()); // エラー、何かキャプチャしています。
void g2(int = ([i]{ return 0; })()); // エラー、何かキャプチャしています。
void g3(int = ([=]{ return i; })()); // エラー、何かキャプチャしています。
void g4(int = ([=]{ return 0; })()); // OK、キャプチャなし。
void g5(int = ([]{ return sizeof i; })()); // OK、キャプチャなし。
}
演算子関数はデフォルト引数を持ってはなりません。 ただし関数呼び出し演算子の場合は除きます。
class C {
int operator[](int i = 0); // ill-formed。
int operator()(int x = 0); // OK。
};
欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
| DR | 適用先 | 発行時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 2082 | C++14 | default arguments were forbidden to use locals in unevaluated context | unevaluated context use allowed |