4

最小代码:

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)这是一个演示

问题

  1. 为什么甚至 (2) 和 (4) 都被视为声明?
  2. foo陈述(3)和(4)中的描述是什么?
4

2 回答 2

1

在解析可能是声明或表达式的构造时 - 被称为最令人烦恼的解析歧义 - 标准说“解决方案是将任何可能是声明的构造视为声明”。

(2) 和 (4) 都是有效的声明,因此它们必须被解析为声明。(3) 和 (4) 都声明了一个foo类型A*()为“不带参数的函数返回指针”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

在上面的最后一个例子中,指向 T 的指针 g 被初始化为 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
}

——结束示例]

于 2013-08-01T17:09:28.120 回答
1

为什么甚至 (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);括号是多余的,因为它们没有改变解析的方式。添加更多它们不会改变任何事情 - 它仍然是一个有效的声明。

语句 (3) 和 (4) 中 foo 的描述是什么?

声明与A* foo();

于 2013-08-01T13:16:36.180 回答