Namespaces
Variants

Default arguments

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Позволяет вызывать функцию без указания одного или нескольких завершающих аргументов.

Обозначается использованием следующего синтаксиса для параметра в 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; 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

Аргументы по умолчанию разрешены только в списках параметров объявлений функций и лямбда-выражений , (начиная с C++11) и не разрешены в объявлениях указателей на функции, ссылок на функции, а также в typedef объявлениях. Списки шаблонных параметров используют аналогичный синтаксис для своих аргументов шаблонов по умолчанию .

Для нетemplate функций аргументы по умолчанию могут быть добавлены к функции, которая уже была объявлена, если функция переобъявляется в той же области видимости. В точке вызова функции аргументы по умолчанию представляют собой объединение аргументов по умолчанию, предоставленных во всех видимых объявлениях функции. Переобъявление не может ввести аргумент по умолчанию для параметра, для которого аргумент по умолчанию уже видим (даже если значение совпадает). Переобъявление во внутренней области видимости не получает аргументы по умолчанию из внешних областей видимости.

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);
}

Если inline функция объявлена в разных единицах трансляции, накопленные наборы аргументов по умолчанию должны быть одинаковыми в конце каждой единицы трансляции.

Если не встроенная функция объявлена в одной и той же области видимости пространства имен в разных единицах трансляции, соответствующие аргументы по умолчанию должны быть одинаковыми, если они присутствуют (но некоторые аргументы по умолчанию могут отсутствовать в некоторых единицах трансляции).

(since 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
                     // значение ::a, которое в этот момент равно 1, не используется
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); // OK согласно CWG 2082
}

Указатель this не допускается в аргументах по умолчанию:

class A
{
    void f(A* p = 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
(since 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 внеклассовые определения функций-членов вложенных классов
шаблонов классов могли иметь аргументы по умолчанию
запрещено