11 Declarators [dcl.decl]

11.3 Meaning of declarators [dcl.meaning]

Декларатор содержит ровно один declarator-id; он называет объявленный идентификатор. unqualified-id Происходит в declarator-id должен быть простым identifier для заявления некоторых специальных функций , за исключением ([class.ctor], [class.conv], [class.dtor], [over.oper]) и для объявления специализаций шаблона или частичных специализаций ([temp.spec]). Когда declarator-id квалифицируется, объявление должно ссылаться на ранее объявленный член класса или пространства имен, к которому относится квалификатор (или, в случае пространства имен, элемента встроенного пространства имен этого пространства имен ([namespace.def])), или на его специализация; член должен не только был введен using-declaration в области видимости класса или пространства имен , выдвинутый nested-name-specifier из declarator-id. Аттестат nested-name-specifierквалифицированного declarator-idне должен начинаться с а decltype-specifier. [ Note: Если квалификатор является​::​ оператором разрешения глобальной области, то declarator-id ссылка на имя, объявленное в области глобального пространства имен. ] Необязательный параметр, следующий за a, относится к объявленной сущности.end noteattribute-specifier-seqdeclarator-id

static, thread_­local, extern, mutable, friend, inline, virtual, constexpr, explicit, Или typedef спецификатор применяется непосредственно к каждому declarator-id в init-declarator-listили member-declarator-list; тип, указанный для каждого, declarator-idзависит как от типа, так decl-specifier-seqи от его declarator.

Таким образом, объявление того или иного идентификатора имеет вид

T D

где T имеет форму и является декларатором. Ниже приводится рекурсивная процедура для определения типа, указанного для содержащегося в таком объявлении.attribute-specifier-seqopt decl-specifier-seqDdeclarator-id

Во-первых, decl-specifier-seq определяет тип. В декларации

T D

decl-specifier-seq T определяет тип T. [ Example: В декларации

int unsigned i;

спецификаторы типа int unsigned определяют тип «unsigned int» ([dcl.type.simple]). ]end example

В объявлении, attribute-specifier-seqopt T D где D - неукрашенный идентификатор, тип этого идентификатора - «T».

В декларации, T D где D есть форма

( D1 )

тип содержимого declarator-id такой же, как и тип содержащегося declarator-id в объявлении

T D1

Круглые скобки не изменяют тип встроенного declarator-id, но они могут изменить привязку сложных деклараторов.

11.3.1 Pointers [dcl.ptr]

В декларации, T D где D есть форма

* attribute-specifier-seqopt cv-qualifier-seqopt D1

и тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D - « указатель на »derived-declarator-type-list cv-qualifier-seqT . Применяется к указателю , а не объект указал. Точно так же необязательный элемент принадлежит указателю, а не указанному объекту.cv-qualifiersattribute-specifier-seq

[ Example: Заявления

const int ci = 10, *pc = &ci, *const cpc = pc, **ppc;
int i, *p, *const cp = &i;

объявить ci, постоянное целое число; pc, указатель на постоянное целое число; cpc, постоянный указатель на постоянное целое число; ppc, указатель на указатель на постоянное целое число; i, целое число; p, указатель на целое число; и cpпостоянный указатель на целое число. Значение ci, cpcи cp не может быть изменено после инициализации. Значение pc может быть изменено, как и объект, на который указывает cp. Примеры некоторых правильных операций:

i = ci;
*cp = ci;
pc++;
pc = cpc;
pc = p;
ppc = &pc;

Примеры плохо сформированных операций:

ci = 1;             // error
ci++;               // error
*pc = 2;            // error
cp = &ci;           // error
cpc++;              // error
p = pc;             // error
ppc = &p;           // error

Каждый из них неприемлем, потому что он либо изменит значение объявленного объекта, const либо позволит позже изменить его с помощью указателя cv-unqualified, например:

*ppc = &ci;         // OK, but would make p point to ci because of previous error
*p = 5;             // clobber ci

end example]

См. Также [expr.ass] и [dcl.init].

[ Note: Формирование указателя на ссылочный тип неверно; см [dcl.ref]. Формирование типа указателя функции неверно, если тип функции имеет cv-qualifiers или ref-qualifier; см [dcl.fct]. Поскольку адрес a bit-field не может быть взят, указатель никогда не может указывать на битовое поле. ]end note

11.3.2 References [dcl.ref]

В объявлении, T D где D есть одна из форм

& attribute-specifier-seqopt D1
&& attribute-specifier-seqopt D1

и тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D - «derived-declarator-type-list ссылка на T». Необязательный параметр attribute-specifier-seqотносится к ссылочному типу. Ссылки с квалификатором cv имеют неправильный формат, за исключением случаев, когда cv-квалификаторы вводятся с использованием символа typedef-name([dcl.typedef], [temp.param]) или decltype-specifier, в этом случае, cv-квалификаторы игнорируются. [Example:

typedef int& A;
const A aref = 3;   // ill-formed; lvalue reference to non-const initialized with rvalue

Типом aref является «ссылка lvalue на int», а не «ссылка lvalue на const int». ] [ Ссылку можно рассматривать как имя объекта. ] Объявление, указывающее тип «ссылка на », имеет неправильный формат.end exampleNote: end note cv void

Ссылочный тип, объявленный с использованием & , называется lvalue reference, а ссылочный тип, объявленный с использованием && , называется rvalue reference. Ссылки Lvalue и ссылки rvalue - это разные типы. Если явно не указано иное, они семантически эквивалентны и обычно называются ссылками.

[Example:

void f(double& a) { a += 3.14; }
// ...
double d = 0;
f(d);

объявляется a ссылочным параметром, f поэтому вызов f(d) будет добавлен 3.14 к d.

int v[20];
// ...
int& g(int i) { return v[i]; }
// ...
g(3) = 7;

объявляет функцию g() для возврата ссылки на целое число, поэтому она g(3)=7 будет назначена 7 четвертому элементу массива v. Другой пример:

struct link {
  link* next;
};

link* first;

void h(link*& p) {  // p is a reference to pointer
  p->next = first;
  first = p;
  p = 0;
}

void k() {
   link* q = new link;
   h(q);
}

объявляет p ссылку на указатель, link поэтому h(q) оставит q нулевое значение. См. Также [dcl.init.ref]. ]end example

Не указано, требуется ли для ссылки storage ([basic.stc]).

Не должно быть ссылок на ссылки, массивов ссылок и указателей на ссылки. Объявление ссылки должно содержать initializer ([dcl.init.ref]), за исключением случаев, когда объявление содержит явный extern спецификатор ([dcl.stc]), является class member объявлением в определении класса или является объявлением параметра или возвращаемого типа ([dcl.fct]); см [basic.def]. Ссылка должна быть инициализирована для ссылки на действительный объект или функцию. [ В частности, пустая ссылка не может существовать в четко определенной программе, потому что единственный способ создать такую ​​ссылку - это привязать ее к «объекту», полученному косвенным путем через нулевой указатель, что вызывает неопределенное поведение. Как описано в , ссылку нельзя напрямую привязать к битовому полю. ]Note: [class.bit]end note

Если typedef-name([dcl.typedef], [temp.param]) или a decltype-specifierобозначает тип, TR который является ссылкой на тип T, попытка создать тип «ссылка lvalue на cv TR» создает тип «ссылка lvalue на T», в то время как попытка создать тип «ссылка rvalue на cv TR» создает тип TR. [ Note: Это правило известно как сворачивание ссылок. ] [ end noteExample:

int i;
typedef int& LRI;
typedef int&& RRI;

LRI& r1 = i;                    // r1 has the type int&
const LRI& r2 = i;              // r2 has the type int&
const LRI&& r3 = i;             // r3 has the type int&

RRI& r4 = i;                    // r4 has the type int&
RRI&& r5 = 5;                   // r5 has the type int&&

decltype(r2)& r6 = i;           // r6 has the type int&
decltype(r2)&& r7 = i;          // r7 has the type int&

end example]

[ Note: Формирование ссылки на тип функции неверно, если тип функции имеет cv-qualifiers или ref-qualifier; см [dcl.fct]. ]end note

11.3.3 Pointers to members [dcl.mptr]

В декларации, T D где D есть форма

nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt D1

и nested-name-specifier обозначает класс, а тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D - « указатель на член класса типа »derived-declarator-type-list cv-qualifier-seqnested-name-specifierT . Необязательный attribute-specifier-seqэлемент принадлежит указателю на член.

[Example:

struct X {
  void f(int);
  int a;
};
struct Y;

int X::* pmi = &X::a;
void (X::* pmf)(int) = &X::f;
double X::* pmd;
char Y::* pmc;

объявляет pmi, pmf, pmd и ,pmc чтобы быть указателем на член X типа int, указатель на член X типа void(int), указатель на член X типа double и указатель на член Y типа char соответственно. Объявление pmd правильно сформировано, даже если X не имеет членов типа double. Точно так же pmc правильно сформировано объявление, даже если Y это неполный тип. pmi и pmf может использоваться так:

X obj;
// ...
obj.*pmi = 7;       // assign 7 to an integer member of obj
(obj.*pmf)(7);      // call a function member of obj with the argument 7

end example]

Указатель на член не должен указывать на static member класс, член со ссылочным типом или «cv void».

[ Note: См. Также [expr.unary] и [expr.mptr.oper]. Тип «указатель на член» отличается от типа «указатель», то есть указатель на член объявляется только синтаксисом указателя на декларатор члена, а не синтаксисом декларатора указателя. В C ++ нет типа «ссылка на член». ]end note

11.3.4 Arrays [dcl.array]

В декларации, T D где D есть форма

D1 [ constant-expressionopt ] attribute-specifier-seqopt

и тип идентификатора в объявлении T D1 - «derived-declarator-type-list T», тогда тип идентификатора D является типом массива; если тип идентификатора D содержит , значит, программа сформирована неправильно. называется массивом ; этот тип не должен быть ссылочным типом , типом функции или типом абстрактного класса. Если присутствует, это должно быть преобразованное постоянное выражение типа, и его значение должно быть больше нуля. Постоянное выражение определяет из (количество элементов в) массива. Если значение константного выражения равно , массив имеет элементы, пронумерованные до , а тип идентификатора - « массив из ». Объект типа массив содержит непрерывно выделенный непустой набор подобъектов типа . За исключением случаев, указанных ниже, если постоянное выражение опущено, тип идентификатора - « массив с неизвестной границей », неполный тип объекта. Тип « массив » отличается от типа « массив с неизвестной границей », см . Любой тип формы « массив » настраивается на «массив » и аналогично для «массив неизвестной границы ». Необязательный элемент принадлежит массиву. [ auto type-specifierTelement type cv voidconstant-expression std​::​size_­t boundNN0N-1Dderived-declarator-type-list N TNTDderived-declarator-type-list Tderived-declarator-type-list N Tderived-declarator-type-list T [basic.types]cv-qualifier-seqN TN cv-qualifier-seq TTattribute-specifier-seqExample:

typedef int A[5], AA[2][3];
typedef const A CA;             // type is “array of 5 const inttypedef const AA CAA;           // type is “array of 2 array of 3 const int

end example] [ Note: «Массив » имеет тип с квалификацией cv; см . ]N cv-qualifier-seq T [basic.type.qualifier]end note

Массив может быть построен из одного из основных типов (кроме void), из указателя, из указателя на член, из класса, из типа перечисления или из другого массива.

Когда несколько «массивов» спецификаций являются смежными, создается многомерный массив; может быть опущено только первое из константных выражений, определяющих границы массивов. В дополнение к объявлениям, в которых разрешен неполный тип объекта, в некоторых случаях при объявлении параметра функции ([dcl.fct]) может быть опущена граница массива . Связь с массивом также может быть опущена, если за декларатором следует initializerили когда за декларатором для статического члена данных следует brace-or-equal-initializer([class.mem]). В обоих случаях граница вычисляется из числа исходных элементов (скажем, N) предоставленных ([dcl.init.aggr]), а тип идентификатора D - «массив N T». Кроме того, если есть предыдущее объявление объекта в той же области, в которой была указана привязка, пропущенная граница массива считается такой же, как в этом более раннем объявлении, и аналогично для определения статического члена данных объекта класс.

[Example:

float fa[17], *afp[17];

объявляет массив float чисел и массив указателей на float числа. Другой пример:

static int x3d[3][5][7];

объявляет статический трехмерный массив целых чисел с рангом 3×5×7. В полной мере, x3d это массив из трех элементов; каждый элемент представляет собой массив из пяти массивов; каждый из последних массивов представляет собой массив из семи целых чисел. Любое из выражений x3d, x3d[i], x3d[i][j], x3d[i][j][k] может разумно появиться в выражении. Наконец-то,

extern int x[10];
struct S {
  static int y[10];
};

int x[];                // OK: bound is 10
int S::y[];             // OK: bound is 10

void f() {
  extern int x[];
  int i = sizeof(x);    // error: incomplete object type
}

end example]

[ Note: Преобразования, влияющие на выражения типа массива, описаны в [conv.array]. Объекты типов массивов не могут быть изменены, см [basic.lval]. ]end note

[ Note: За исключением случаев, когда он был объявлен для класса ([over.sub]), оператор нижнего индекса [] интерпретируется таким же образом, что E1[E2] и *((E1)+(E2)) ([expr.sub]). Из-за правил преобразования, которые применяются к +, if E1 является массивом и E2 целым числом, то E1[E2] относится к E2-ому члену E1. Таким образом, несмотря на асимметричный вид, индексирование является коммутативной операцией. ]end note

[ Note: Для многомерных массивов соблюдается единое правило . Если E является n-мерным массивом ранга i×j××k, то, E появляющееся в выражении, которое подлежит array-to-pointer conversion , преобразуется в указатель на (n1)-мерный массив с рангом j××k. Если * оператор, явно или неявно в результате индексации, применяется к этому указателю, результатом является указанный на (n1)размерный массив, который сам немедленно преобразуется в указатель. [ Example: Учтите

int x[3][5];

Вот x это 3×5 массив целых чисел. Когда x появляется в выражении, оно преобразуется в указатель на (первый из трех) пятичленный массив целых чисел. В выражении, x[i] которое эквивалентно *(x+i), x сначала преобразуется в указатель, как описано; then x+i преобразуется в тип x, который включает умножение i на длину объекта, на который указывает указатель, а именно пяти целочисленных объектов. Результаты складываются, и косвенное обращение применяется для получения массива (из пяти целых чисел), который, в свою очередь, преобразуется в указатель на первое из целых чисел. Если есть другой нижний индекс, тот же аргумент применяется снова; на этот раз результат - целое число. ] ]end exampleend note

[ Note: Из всего этого следует, что массивы в C ++ хранятся построчно (последний индекс изменяется быстрее всего) и что первый индекс в объявлении помогает определить объем памяти, потребляемой массивом, но не играет никакой другой роли в вычислениях индекса. ]end note

11.3.5 Functions [dcl.fct]

В декларации, T D где D есть форма

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt

и тип, содержащийся declarator-id в объявлении, T D1 - «derived-declarator-type-list T», тип declarator-id in D - «derived-declarator-type-list noexceptopt функция возврата ( parameter-declaration-clause) », где необязательный элемент присутствует тогда и только тогда, когда он не выбрасывает. Необязательный параметр относится к типу функции.cv-qualifier-seqopt ref-qualifieropt T noexcept exception specification attribute-specifier-seq

В декларации, T D где D есть форма

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-type

и тип, содержащийся declarator-id в объявлении T D1 «derived-declarator-type-list T», T должен быть единственным type-specifier auto. Тип declarator-id in D - это «derived-declarator-type-list noexceptopt функция возврата ( parameter-declaration-clause) », где - тип, указанный в , и где необязательный параметр присутствует тогда и только тогда, когда спецификация исключения не выбрасывает. Необязательный параметр относится к типу функции.cv-qualifier-seqopt ref-qualifieropt U U trailing-return-type noexcept attribute-specifier-seq

parameter-declaration-clause Определяет аргументы , которые могут быть определены, и их обработка, когда функция вызывается. [ Используется для преобразования аргументов , указанных в вызове функции; см . ] Если пусто, функция не принимает аргументов. Список параметров, состоящий из одного безымянного параметра независимого типа , эквивалентен пустому списку параметров. За исключением этого особого случая, у параметра не должно быть типа . Если завершается многоточием или a , количество аргументов должно быть равно или превышать количество параметров, которые не имеют аргумента по умолчанию и не являются пакетами параметров функции. Там, где синтаксически правильно и где « » не является частью , « » является синонимом « ». [ ДекларацияNote: parameter-declaration-clause [expr.call]end noteparameter-declaration-clause void cv voidparameter-declaration-clause function parameter pack...abstract-declarator, ......Example:

int printf(const char*, ...);

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

printf("hello world");
printf("a=%d b=%d", a, b);

Однако первый аргумент должен иметь тип, который может быть преобразован в ] [ Стандартный заголовок содержит механизм доступа к аргументам, переданным с использованием многоточия (см. И ). ]const char* end exampleNote: <cstdarg> [expr.call] [support.runtime]end note

Одно имя может использоваться для нескольких различных функций в одной области; это функция overloading. Все объявления функции должны точно согласовываться как в типе возвращаемого значения, так и в списке типов-параметров. Тип функции определяется по следующим правилам. Тип каждого параметра (включая пакеты параметров функций) определяется его собственным decl-specifier-seq и declarator. После определения типа каждого параметра любой параметр типа «массив T» или типа функции T корректируется как «указатель на T». После создания списка типов параметров любые изменения верхнего уровня, cv-qualifiers изменяющие тип параметра, удаляются при формировании типа функции. Результирующий список преобразованных типов параметров и наличие или отсутствие многоточия или пакета параметров функции - это функция parameter-type-list. [ Note: Это преобразование не влияет на типы параметров. Например, int(*)(const int p, decltype(p)*) и int(*)(int, const int*) идентичные типы. ] end note

Тип функции с символом a cv-qualifier-seqили a ref-qualifier(включая тип с именем typedef-name([dcl.typedef], [temp.param])) должен отображаться только как:

[Example:

typedef int FIC(int) const;
FIC f;              // ill-formed: does not declare a member function
struct S {
  FIC f;            // OK
};
FIC S::*pm = &S::f; // OK

end example]

Эффект от a cv-qualifier-seq в деклараторе функции отличается от добавления cv-квалификации поверх типа функции. В последнем случае cv-квалификаторы игнорируются. [ Note: Тип функции, имеющий a cv-qualifier-seq, не является типом cv; не существует типов функций с квалификацией cv. ] [ end noteExample:

typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
};

end example]

Тип возвращаемого значения, список типов-параметров, the ref-qualifier, the cv-qualifier-seqи спецификация исключения, но не the default arguments, являются частью типа функции. [ Note: Типы функций проверяются во время присвоения и инициализации указателей на функции, ссылок на функции и указателей на функции-члены. ]end note

[ ДекларацияExample:

int fseek(FILE*, long, int);

объявляет функцию, принимающую три аргумента указанных типов и возвращающую int ([dcl.type]). ]end example

Функции не должны иметь возвращаемый тип массива или функции типа, хотя они могут иметь возвращаемый тип указателя типа или ссылки на такие вещи. Массивов функций быть не должно, хотя могут быть массивы указателей на функции.

Типы не должны определяться в возвращаемых типах или типах параметров. Тип параметра или возвращаемый тип для определения функции не должен быть неполным (возможно, cv-квалифицированным) типом класса в контексте определения функции, если только функция не является deleted.

Typedef типа функции может использоваться для объявления функции, но не должен использоваться define a function. [Example:

typedef void F();
F  fv;              // OK: equivalent to void fv();
F  fv { }           // ill-formed
void fv() { }       // OK: definition of fv

end example]

Идентификатор может быть дополнительно предоставлен как имя параметра; если присутствует в function definition, он называет параметр. [ Note: В частности, имена параметров также являются необязательными в определениях функций и именах, используемых для параметра в разных объявлениях, и определение функции не обязательно должно быть одинаковым. Если имя параметра присутствует в объявлении функции, которое не является определением, его нельзя использовать вне декларатора функции, потому что это степень его потенциальной области видимости ([basic.scope.proto]). ]end note

[ Example: Декларация

int i,
    *pi,
    f(),
    *fpi(int),
    (*pif)(const char*, const char*),
    (*fpif(int))(int);

объявляет целое число i, указатель pi на целое число, функцию f , не принимающую аргументов и возвращающую целое число, функцию, fpi принимающую целочисленный аргумент и возвращающую указатель на целое число, указатель pif на функцию, которая принимает два указателя на постоянные символы и возвращает целое число , функция, fpif принимающая целочисленный аргумент и возвращающая указатель на функцию, которая принимает целочисленный аргумент и возвращает целое число. Особенно полезно сравнивать fpi и pif. Привязка *fpi(int) is *(fpi(int)), поэтому объявление предполагает, и та же конструкция в выражении требует вызова функцииfpi, а затем использования косвенного обращения через результат (указатель) для получения целого числа. В деклараторе (*pif)(const char*, const char*)дополнительные круглые скобки необходимы, чтобы указать, что косвенное обращение через указатель на функцию приводит к функции, которая затем вызывается. ] [ Typedefs и иногда удобен, когда тип возвращаемого значения функции сложный. Например, указанная выше функция могла быть объявленаend exampleNote: trailing-return-types fpif

typedef int  IFUNC(int);
IFUNC*  fpif(int);

или

auto fpif(int)->int(*)(int);

A trailing-return-typeнаиболее полезен для типа, который было бы сложнее указать перед declarator-id:

template <class T, class U> auto add(T t, U u) -> decltype(t + u);

скорее, чем

template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);

end note]

A non-template function - это функция, которая не является специализацией шаблона функции. [ Note: Шаблон функции не является функцией. ] end note

A declarator-idили abstract-declarator содержащие многоточие должны использоваться только в parameter-declaration. Такой parameter-declarationвот parameter pack. Когда он является частью parameter-declaration-clause, пакет параметров - это function parameter pack. [ Note: В противном случае, parameter-declarationявляется частью a, template-parameter-listа пакет параметров является пакетом параметров шаблона; см [temp.param]. ] Пакет параметров функции - это файл . [ end note pack expansionExample:

template<typename... T> void f(T (* ...t)(int, int));

int add(int, int);
float subtract(int, int);

void g() {
  f(add, subtract);
}

end example]

Возникает синтаксическая двусмысленность, когда многоточие стоит в конце a parameter-declaration-clauseбез предшествующей запятой. В этом случае многоточие анализируется как часть, abstract-declaratorесли тип параметра либо называет пакет параметров шаблона, который не был развернут, либо содержит auto; в противном случае он анализируется как часть parameter-declaration-clause.101

Как указано в синтаксисе, cv-квалификаторы являются важным компонентом в типах возвращаемых функций.

Можно явно устранить неоднозначность синтаксического анализа, введя запятую (так что многоточие будет анализироваться как часть parameter-declaration-clause) или введя имя для параметра (так, чтобы многоточие было проанализировано как часть declarator-id).

11.3.6 Default arguments [dcl.fct.default]

Если initializer-clauseв a указано, parameter-declarationэто initializer-clause используется как аргумент по умолчанию. Аргументы по умолчанию будут использоваться в вызовах, в которых отсутствуют конечные аргументы.

[ Example: Декларация

void point(int = 3, int = 4);

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

point(1,2);  point(1);  point();

Последние два вызова эквивалентны point(1,4) и point(3,4)соответственно. ]end example

Аргумент по умолчанию должен быть указан только в parameter-declaration-clause объявлении функции lambda-declarator или в template-parameter; в последнем случае initializer-clauseдолжен быть assignment-expression. Аргумент по умолчанию не должен указываться для пакета параметров. Если это указано в a parameter-declaration-clause, это не должно происходить в пределах a declarator или abstract-declarator a parameter-declaration.102

Для функций, не являющихся шаблоном, аргументы по умолчанию могут быть добавлены в более поздние объявления функции в той же области. Объявления в разных областях имеют совершенно разные наборы аргументов по умолчанию. То есть объявления во внутренних областях не получают аргументы по умолчанию из объявлений во внешних областях, и наоборот. В заданном объявлении функции каждый параметр, следующий за параметром с аргументом по умолчанию, должен иметь аргумент по умолчанию, предоставленный в этом или предыдущем объявлении, или должен быть пакетом параметров функции. Аргумент по умолчанию не должен переопределяться более поздним объявлением (даже с тем же значением). [Example:

void g(int = 0, ...);           // OK, ellipsis is not a parameter so it can follow
                                // a parameter with a default argument
void f(int, int);
void f(int, int = 7);
void h() {
  f(3);                         // OK, calls f(3, 7)
  void f(int = 1, int);         // error: does not use default from surrounding scope
}
void m() {
  void f(int, int);             // has no defaults
  f(4);                         // error: wrong number of arguments
  void f(int, int = 5);         // OK
  f(4);                         // OK, calls f(4, 5);
  void f(int, int = 5);         // error: cannot redefine, even to same value
}
void n() {
  f(6);                         // OK, calls f(6, 7)
}

end example] Для данной встроенной функции, определенной в разных единицах трансляции, накопленные наборы аргументов по умолчанию в конце единиц трансляции должны быть одинаковыми; см [basic.def.odr]. Если объявление друга указывает выражение аргумента по умолчанию, это объявление должно быть определением и должно быть единственным объявлением функции или шаблона функции в блоке перевода.

Аргумент по умолчанию имеет те же семантические ограничения, что и инициализатор в объявлении переменной типа параметра с использованием copy-initialization семантики. Имена в аргументе по умолчанию связываются, и семантические ограничения проверяются в точке, где появляется аргумент по умолчанию. Поиск имени и проверка семантических ограничений для аргументов по умолчанию в шаблонах функций и в функциях-членах шаблонов классов выполняются, как описано в [temp.inst]. [ Example: В следующем коде g будет вызываться со значением f(2):

int a = 1;
int f(int);
int g(int x = f(a));            // default argument: f(​::​a)

void h() {
  a = 2;
  {
  int a = 3;
  g();                          // g(f(​::​a))
  }
}

end example] [ Note: В объявлениях функций-членов ищутся имена в аргументах по умолчанию, как описано в [basic.lookup.unqual]. Проверка доступа применяется к именам в аргументах по умолчанию, как описано в разделе [class.access]. ]end note

За исключением функций-членов шаблонов классов, аргументы по умолчанию в определении функции-члена, которое появляется за пределами определения класса, добавляются к набору аргументов по умолчанию, предоставленных объявлением функции-члена в определении класса; программа имеет неправильный формат, если так объявлен конструктор по умолчанию ([class.ctor]), конструктор копирования или перемещения или оператор присваивания копирования или перемещения ([class.copy]). Аргументы по умолчанию для функции-члена шаблона класса должны быть указаны в начальном объявлении функции-члена в шаблоне класса. [Example:

class C {
  void f(int i = 3);
  void g(int i, int j = 99);
};

void C::f(int i = 3) {}         // error: default argument already specified in class scope
void C::g(int i = 88, int j) {} // in this translation unit, C​::​g can be called with no argument

end example]

Локальная переменная не должна появляться как потенциально оцениваемое выражение в аргументе по умолчанию. [Example:

void f() {
  int i;
  extern void g(int x = i);         // error
  extern void h(int x = sizeof(i)); // OK
  // ...
}

end example]

[ Note: Ключевое слово this может не появляться в аргументе по умолчанию функции-члена; см [expr.prim.this]. [Example:

class A {
  void f(A* p = this) { }           // error
};

end example] ]end note

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

int a;
int f(int a, int b = a);            // error: parameter a used as default argument
typedef int I;
int g(float I, int b = I(2));       // error: parameter I found
int h(int a, int b = sizeof(a));    // OK, unevaluated operand

end example] Член нестатический не должен появляться в аргументе по умолчанию , если он не появляется , как id-expressionиз class member access expression или , если он не используется для формирования указателя на член ([expr.unary.op]). [ Example: Объявление X​::​mem1() в следующем примере неверно сформировано, поскольку не предоставляется объект для нестатического члена, X​::​a используемого в качестве инициализатора.

int b;
class X {
  int a;
  int mem1(int i = a);              // error: non-static member a used as default argument
  int mem2(int i = b);              // OK;  use X​::​b
  static int b;
};

Однако объявление of X​::​mem2() имеет смысл, поскольку для доступа к статическому члену не требуется никаких объектов X​::​b. Классы, объекты и члены описаны в разделе [class]. ] Аргумент по умолчанию не является частью типа функции. [end exampleExample:

int f(int = 0);

void h() {
  int j = f(1);
  int k = f();                      // OK, means f(0)
}

int (*p1)(int) = &f;
int (*p2)() = &f;                   // error: type mismatch

end example] Когда объявление функции вводится через a using-declaration, также становится известна любая информация аргумента по умолчанию, связанная с объявлением. Если после этого функция повторно объявляется в пространстве имен с дополнительными аргументами по умолчанию, дополнительные аргументы также становятся известными в любой момент после повторного объявления, где using-declaration находится в области видимости.

virtual function Вызов использует аргументы по умолчанию в объявлении виртуальной функции , определяемой статическим типом указателя или ссылки , обозначающего объект. Переопределяющая функция в производном классе не получает аргументы по умолчанию от функции, которую она переопределяет. [Example:

struct A {
  virtual void f(int a = 7);
};
struct B : public A {
  void f(int a);
};
void m() {
  B* pb = new B;
  A* pa = pb;
  pa->f();          // OK, calls pa->B​::​f(7)
  pb->f();          // error: wrong number of arguments for B​::​f()
}

end example]

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