Минимальный код:
struct A {
A(int = 0) {}
};
int i = 0, *p = &i;
int* foo () { return p; }
int main () {
A(); // calls `A::A(int=0)`
A(i); // calls `A::A(int=0)`
A(*p); // <--- (1) same as local `A *p;`
{
A((*p)); // <--- (2) same as local `A *p;`
}
A (*foo()); // <--- (3) ??
{
A ((*foo())); // <--- (4) ??
}
}
Ожидал хотя бы A((*p))
будет ссылаться A::A(int=0)
, Даже положить несколько скобок вокруг *p
, трактует заявление как A *p;
,
То же самое относится и к foo
родственное утверждение, где конструктор A::A(int=0)
не называется.
Вот демонстрация.
Вопрос:
foo
в утверждениях (3) и (4)?При синтаксическом анализе конструкции, которая может быть либо объявлением, либо выражением — известным как двусмысленность синтаксического анализа — стандарт говорит: «Решение состоит в том, чтобы рассмотреть любую конструкцию, которая может быть объявлением декларацией».
Оба (2) и (4) являются действительными объявлениями, поэтому они должны быть проанализированы как объявления. Оба (3) и (4) объявляют функцию foo
типа A*()
aka «функция, не принимающая параметров, возвращающая указатель на A
«
6.8 Ambiguity resolution [stmt.ambig]
В грамматике существует двусмысленность, связанная с выражениями-операторами и объявлениями: выражение-выражение с явным преобразованием типа в стиле функции (5.2.3) в качестве крайнего левого подвыражения может быть неотличимо от объявления, где первый декларатор начинается с ((. В этих случаях заявление является декларацией. [Примечание: Чтобы устранить неоднозначность, может потребоваться проверка всего утверждения, чтобы определить, является ли оно выражением-выражением или объявлением. Это устраняет неоднозначность многих примеров. [Пример: предполагая, что T является спецификатором простого типа (7.1.5),
T(a)->m = 7; // expression-statement
T(a)++; //expression-statement
T(a,5)<<c; //expression-statement
T(*d)(int); //declaration
T(e)[5]; //declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration
В последнем примере выше, g, который является указателем на T, инициализируется как double (3). Это, конечно, неосознанно по смысловым причинам, но это не влияет на синтаксический анализ. — конец примера]
8.2 Ambiguity resolution [dcl.ambig.res]
Неоднозначность, возникающая из-за сходства между приведением стиля функции и объявлением, упомянутым в 6.8, также может происходить в контексте объявления. В этом контексте выбор делается между объявлением функции с избыточным набором скобок вокруг имени параметра и объявлением объекта со стилем функции, приведенным в качестве инициализатора. Как и в случае двусмысленности, упомянутой в 6.8, решение состоит в том, чтобы рассмотреть любую конструкцию, которая может быть декларацией декларацией. [Примечание: объявление может быть явно устранено неоднозначностью с помощью приведения типа, не являющегося функцией, с помощью = для указания инициализации или путем удаления лишних скобок вокруг имени параметра. ] [Пример:
struct S {
S(int);
};
void foo(double a)
{
S w(int(a)); // function declaration
S x(int()); // function declaration
S y((int)a); // object declaration
S z = int(a); // object declaration
}
— конец примера]
Почему даже (2) и (4) рассматриваются как декларации?
Скобки в объявлениях могут использоваться для изменения порядка ассоциации в объявлениях и, возможно, для изменения значения конструкции от объявления к выражению. Они имеют такой же приоритет, как []
и группа слева направо.
Например:
int*a[1]; // brackets have higher precedence - this is an array of pointers
int(*a)[1]; // pointer to an array
Теперь, если вы считаете, A*p;
а также A(*p);
круглые скобки здесь излишни, потому что они не изменили способ синтаксического анализа. Добавление большего количества из них ничего не меняет — это все еще действительная декларация.
Каково описание foo в утверждениях (3) и (4)?
Декларации такие же, как A* foo();