Default arguments
関数が1つ以上の末尾引数を指定せずに呼び出せるようにします。
以下の構文を parameter-list 内のパラメータに使用することで示されます。これは function declaration の一部です。
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; instantiates declaration 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, ...); // 可
デフォルト引数は、 関数宣言 および lambda-expressions のパラメータリストでのみ許可され、 (C++11以降) 関数へのポインタ、関数への参照の宣言、あるいは 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); // エラー: 2番目のパラメータのデフォルト引数は // 外側のスコープから継承されない } 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); // エラー: 2番目のパラメータは既に // デフォルト引数を持っている(値が同じ場合でも) } void f(int = 1, int); // #3 OK, #2にデフォルト引数を追加 void n() { // 新しいスコープ開始 f(); // #1, #2, #3がスコープ内: f(1, 7)を呼び出す; }
もし inline 関数が異なる翻訳単位で宣言された場合、デフォルト引数の累積集合は各翻訳単位の終端で同一でなければなりません。
|
異なる翻訳単位で同じ名前空間スコープ内で非インライン関数が宣言される場合、対応するデフォルト引数が存在する場合は同じでなければならない(ただし、一部のデフォルト引数は一部のTUで欠如していてもよい)。 |
(C++20以降) |
もし friend 宣言がデフォルト引数を指定する場合、それはfriend関数の定義でなければならず、この関数の他の宣言は翻訳単位で許可されません。
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を見つける // この時点で値が1である ::aの値は使用されない void h() { a = 2; // ::aの値を変更する { int a = 3; g(); // f(2)を呼び出し、その結果でg()を呼び出す } }
非テンプレート化されたクラスの メンバー関数 に対して、クラス外定義でのデフォルト引数が許可されており、クラス本体内部の宣言で提供されたデフォルト引数と結合されます。これらのクラス外デフォルト引数によってメンバー関数がデフォルトコンストラクタまたはコピー /ムーブ (C++11以降) コンストラクタ/代入演算子に変わる場合(呼び出しが曖昧になるため)、プログラムは不適格となります。テンプレート化されたクラスのメンバー関数については、すべてのデフォルト引数はメンバー関数の最初の宣言で提供されなければなりません。
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) {} // エラー: これをデフォルトコンストラクタに変換してしまう
virtual 関数のオーバーライドは、基底クラスの宣言からデフォルト引数を継承せず、仮想関数呼び出しが行われる際、デフォルト引数はオブジェクトの静的な型に基づいて決定されます(注: これは non-virtual interface パターンで回避可能です)。
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(); // エラー: デフォルト引数が存在しない }
ローカル変数は、それらが 評価されない 場合を除き、デフォルト引数では許可されません:
void f() { int n = 1; extern void g(int x = n); // エラー: ローカル変数はデフォルト引数として使用できません extern void h(int x = sizeof n); // 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が見つかります int mem3(int X::* i = &X::a); // OK: 非静的メンバーが使用できます int mem4(int i = x.a); // OK: メンバーアクセス式内で使用 static X x; static int b; };
デフォルト引数は、対応するパラメータに対して引数が指定されずに関数が呼び出されるたびに評価されます。関数パラメータは、それらが 評価されない場合 を除き、デフォルト引数では使用できません。パラメータリストで前に現れるパラメータが スコープ内 にあることに注意してください:
int a; int f(int a, int b = a); // エラー: デフォルト引数でパラメータaが使用されている int g(int a, int b = sizeof a); // 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)である
演算子関数のうち、 関数呼び出し演算子 および 添字演算子 (C++23以降) 以外は、デフォルト引数を持つことができません:
class C { int operator++(int i = 0); // 不正な形式 int operator[](int j = 0); // C++23以降で有効 int operator()(int k = 0); // 有効 };
|
明示的オブジェクトパラメータ はデフォルト引数を持つことができません: struct S { void f(this const S& = S{}); }; // ill-formed |
(C++23以降) |
注記
パラメータ名が存在しない場合、複合代入トークンを避けるためにスペースが必要になることがあります( maximal munch を参照)。
void f1(int*=0); // エラー、「*=」はここでは予期しないトークンです void g1(const int&=0); // エラー、「&=」はここでは予期しないトークンです void f2(int* = 0); // OK void g2(const int& = 0); // OK void h(int&&=0); // スペースがなくてもOK、「&&」はここでは単一のトークンです
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用バージョン | 公開時の仕様 | 修正後の仕様 |
|---|---|---|---|
| CWG 217 | C++98 |
クラステンプレートの非テンプレートメンバー関数に
デフォルト引数を追加できた |
禁止 |
| CWG 1344 | C++98 |
メンバー関数のクラス外定義で追加されたデフォルト引数が
特殊メンバー関数への変更を引き起こす可能性があった |
禁止 |
| CWG 1716 | C++98 |
デフォルト引数は、呼び出し元が引数を指定した場合でも
関数が呼び出されるたびに評価されていた |
対応するパラメータに引数が
指定されない場合のみ 評価される |
| CWG 2082 | C++98 |
デフォルト引数で未評価コンテキストにおける
ローカル変数と前方パラメータの使用が禁止されていた |
未評価コンテキストでの
使用を許可 |
| CWG 2233 | C++11 |
パラメータパックから展開されたパラメータは
デフォルト引数を持つパラメータの後に出現できなかった |
許可 |
| CWG 2683 | C++98 |
クラステンプレートのネストされたクラスのメンバー関数の
クラス外定義でデフォルト引数を持てた |
禁止 |