临时函数调用的构造被解释为声明

时间:2015-01-19 22:40:17

标签: c++ temporary most-vexing-parse construction

最近我遇到了一个问题,不知何故(但只是某种程度上)对我有意义。它基于解释临时的构造作为单个(!)构造函数参数的声明。请看下面的最小例子。

#include <iostream>

class Foo0{
public:
  Foo0(int a){};
  void doStuff() {std::cout<<"maap"<<std::endl;};
};

class Foo1{
public:
  Foo1(int a){};
  void doStuff() {std::cout<<"maap"<<std::endl;};
};

class Foo2{
public:
  Foo2(int a){};
  void doStuff() {std::cout<<"maap"<<std::endl;};
};

class Bar{
public:
  Bar(Foo0 foo0, Foo1 foo1, Foo2 foo2){};
};

int main () {
  int x = 1;

  Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’
  Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF
  Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF
  Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’
  Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me

  x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious though.
}

我已经读过如下表达式:

Foo(a);

解释(如果有标准构造函数)作为a的声明。这是有道理的,它完全没问题,因为你可以使用{} -brackets来明确构造结构。但我不明白的是:

  1. 为什么bar0的构造有问题? 所有Foo都没有标准构造函数。因此,将Foo0(x)之类的内容解释为x的声明是没有意义的。

  2. 为什么bar1bar2的构建有效? 很明显,bar4的构造是有效的,因为我对所有临时Foo使用了{} -brackets,因此我明确了我想要的内容。

  3. 如果只需要使用{} -brackets只有一个Foo来解决问题...为什么bar3的构造失败?

  4. 此外,在构造任何Bar之前声明x。为什么编译器不抱怨?

  5. 最后一个问题与我最后一行示例代码有关。长话短说:编译器认为我希望他做什么,我在哪里想念阴影的外观?

    PS:如果感兴趣的话 - 我使用gcc-4.9.2 PPS:我尝试使用bar的构造函数将三个Foo0作为参数。同样的故事在这里但是错误没有说明有关冲突的声明,而是关于重新定义x

2 个答案:

答案 0 :(得分:10)

规则是如果声明具有函数声明的语法,那么它就是一个;否则它是一个变量声明。令人惊讶的例子有时被称为

Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’

这是一个函数声明:bar0是名称,Bar是返回类型,参数类型是Foo0Foo1Foo2 。参数名称都是x,这是非法的 - 函数参数的名称必须不同。如果您将x x x更改为x y z,则错误会消失。)

Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF
Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF
Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me

这些行并创建bar1类型的对象bar2bar4Bar。它们不能被解析为函数声明,因为{ }表示法在函数声明中不是有效语法。

因此,Foo0{x}等是为Bar的构造函数提供参数的表达式。 Foo0{x}Foo0(x)是使用初始化程序Foo0声明临时类型x的等效方法。

Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’

我认为这是编译错误;部分Foo2{x}表示该行不能是函数声明;它看起来像是变量bar3的有效声明。

x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious

xint;它没有任何方法。

答案 1 :(得分:2)

1)在这种情况下,Foo0(x)被视为函数bar0的参数。在这里,它是否具有标准构造函数并不重要。它不是局部变量声明和初始化,而只是函数声明中的参数声明。

2)我的猜测是这与解析有关,但如果我错了,有人会纠正我。例子bar1和bar2工作,因为编译器知道bar1和bar2是局部变量声明(而不是函数)声明一见到{}的第一次出现。这些第一次出现的{}出现在x被声明为函数的两个参数之前。

3)bar3的构造失败,因为编译器首先假设bar3是函数声明。函数声明有三个参数,都是x。显然,这是不正确的。

4)函数声明中的x只是参数的名称。它与您之前声明的整数x的范围不同。