名稱空間
變體
操作

預設引數

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

允許在不提供一個或多個尾隨引數的情況下呼叫函式。

透過在函式宣告引數列表中為引數使用以下語法來指示。

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); // calls point(1, 2)
point(1);    // calls point(1, 4)
point();     // calls point(3, 4)

在函式宣告中,在帶有預設引數的引數之後,所有後續引數都必須

  • 在此或同一範圍內的先前宣告中提供預設引數
int x(int = 1, int); // Error: only the trailing parameters can have default arguments
                     //        (assuming there's no previous declaration of “x”)
 
void f(int n, int k = 1);
void f(int n = 0, int k); // OK: the default argument of “k” is provided by
                          // the previous declaration in the same scope
 
void g(int, int = 7);
 
void h()
{
    void g(int = 1, int); // Error: not the same scope
}
  • ...除非引數是從引數包展開的
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, ...); // OK

預設引數只允許在函式宣告lambda表示式的引數列表(C++11 起)中,並且不允許在函式指標、函式引用或typedef宣告中。模板引數列表為其預設模板引數使用類似的語法。

對於非模板函式,如果函式在同一作用域內被重新宣告,則可以將預設引數新增到已宣告的函式。在函式呼叫點,預設引數是該函式所有可見宣告中提供的預設引數的並集。重新宣告不能為已可見預設引數的引數引入新的預設引數(即使值相同)。內部作用域的重新宣告不會繼承外部作用域的預設引數。

void f(int, int);     // #1
void f(int, int = 7); // #2 OK: adds a default argument
 
void h()
{
    f(3); // #1 and #2 are in scope; makes a call to f(3,7)
    void f(int = 1, int); // Error: the default argument of the second
                          // parameter is not acquired from outer scopes
}
 
void m()
{ // new scope begins
    void f(int, int); // inner scope declaration; has no default argument.
    f(4); // Error: not enough arguments to call f(int, int)
    void f(int, int = 6);
    f(4); // OK: calls f(4, 6);
    void f(int, int = 6); // Error: the second parameter already has a
                          // default argument (even if the values are the same)
}
 
void f(int = 1, int); // #3 OK, adds a default argument to #2
 
void n()
{ // new scope begins
    f(); // #1, #2, and #3 are in scope: calls f(1, 7);
}

如果內聯函式在不同的翻譯單元中宣告,則在每個翻譯單元結束時,預設引數的累積集合必須相同。

如果非行內函數在不同的翻譯單元中以相同的名稱空間範圍宣告,則如果存在對應的預設引數,它們必須相同(但某些預設引數可以在某些翻譯單元中缺失)。

(C++20 起)

如果友元宣告指定了預設引數,則它必須是友元函式定義,並且在此函式在翻譯單元中不允許有其他宣告。

using-宣告會繼承已知的預設引數集,並且如果稍後向函式的名稱空間添加了更多預設引數,則這些預設引數也會在使用宣告可見的任何地方可見

namespace N
{
    void f(int, int = 1);
}
 
using N::f;
 
void g()
{
    f(7); // calls f(7, 1);
    f();  // error
}
 
namespace N
{
    void f(int = 2, int);
}
 
void h()
{
    f();  // calls f(2, 1);
}

預設引數中使用的名稱在宣告點進行查詢、訪問性檢查和繫結,但在函式呼叫點執行

int a = 1;
 
int f(int);
 
int g(int x = f(a)); // lookup for f finds ::f, lookup for a finds ::a
                     // the value of ::a, which is 1 at this point, is not used
 
void h()
{
    a = 2; // changes the value of ::a
    {
        int a = 3;
        g(); // calls f(2), then calls g() with the result
    }
}

對於非模板化類的成員函式,預設引數允許在類外定義上,並與類體內部宣告提供的預設引數結合。如果這些類外預設引數將成員函式變為預設建構函式或複製/移動(C++11 起)建構函式/賦值運算子(這會導致呼叫歧義),則程式格式不正確。對於模板化類的成員函式,所有預設引數必須在成員函式的初始宣告中提供。

class C
{
    void f(int i = 3);
    void g(int i, int j = 99);
    C(int arg); // non-default constructor
};
 
void C::f(int i = 3) {}         // error: default argument already
                                // specified in class scope
 
void C::g(int i = 88, int j) {} // OK: in this translation unit,
                                // C::g can be called with no argument
 
C::C(int arg = 1) {}            // Error: turns this into a default constructor

函式的重寫器不會從基類宣告中獲取預設引數,並且在進行虛擬函式呼叫時,預設引數是根據物件的靜態型別決定的(注意:這可以透過非虛介面模式來避免)。

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: calls Derived::f(7)
    d.f(); // Error: no default argument
}

區域性變數不允許在預設引數中,除非它們未被求值

void f()
{
    int n = 1;
    extern void g(int x = n); // error: local variable cannot be a default argument
    extern void h(int x = sizeof n); // OK as of CWG 2082
}

this 指標不允許在預設引數中

class A
{
    void f(A* p = this) {} // error: this is not allowed
};

非靜態類成員不允許在預設引數中(即使它們未被求值),除非用於形成指向成員的指標或在成員訪問表示式中

int b;
 
class X
{
    int a;
    int mem1(int i = a); // error: non-static member cannot be used
    int mem2(int i = b); // OK: lookup finds X::b, the static member
    int mem3(int X::* i = &X::a); // OK: non-static member can be used
    int mem4(int i = x.a); // OK: in a member access expression
 
    static X x;
    static int b;
};

每次函式呼叫時,如果對應的引數沒有提供引數,則會求值預設引數。函式引數不允許在預設引數中,除非它們未被求值。請注意,引數列表中較早出現的引數在作用域

int a;
 
int f(int a, int b = a); // Error: the parameter a used in a default argument
 
int g(int a, int b = sizeof a); // Error until resolving CWG 2082
                                // OK after resolution: use in unevaluated context is OK

預設引數不屬於函式型別

int f(int = 0);
 
void h()
{
    int j = f(1);
    int k = f(); // calls f(0);
}
 
int (*p1)(int) = &f;
int (*p2)()    = &f; // Error: the type of f is int(int)

除了函式呼叫運算子下標運算子(C++23 起)之外,運算子函式不能有預設引數

class C
{
    int operator++(int i = 0); // ill-formed
    int operator[](int j = 0); // OK since C++23
    int operator()(int k = 0); // OK
};

顯式物件引數不能有預設引數

struct S { void f(this const S& = S{}); }; // ill-formed
(C++23 起)

[編輯] 注意

如果引數名缺失,可能需要空格以避免複合賦值記號(參見最大貪吃)。

void f1(int*=0);         // Error, “*=” is unexpected here
void g1(const int&=0);   // Error, “&=” is unexpected here
void f2(int* = 0);       // OK
void g2(const int& = 0); // OK
void h(int&&=0);         // OK even without spaces, “&&” is a token here

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 217 C++98 可以向非
模板類模板的成員函式新增預設引數
已禁止
CWG 1344 C++98 在成員函式的類外定義中新增的預設引數
可能會將其更改為特殊成員函式
已禁止
CWG 1716 C++98 預設引數在每次函式呼叫時都會被求值,
即使呼叫者提供了引數
僅在未為
對應引數提供
引數時才求值
CWG 2082 C++98 預設引數禁止在未求值上下文中
使用區域性變數和前面的引數
未求值上下文
允許使用
CWG 2233 C++11 從引數包展開的引數不能
出現在帶有預設引數的引數之後
允許
CWG 2683 C++98 類模板巢狀類的成員函式的類外定義
可以有預設引數
已禁止