最近我遇到了一个问题,不知何故(但只是某种程度上)对我有意义。它基于解释临时的构造作为单个(!)构造函数参数的声明。请看下面的最小例子。
#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来明确构造结构。但我不明白的是:
为什么bar0的构造有问题?
所有Foo
都没有标准构造函数。因此,将Foo0(x)
之类的内容解释为x
的声明是没有意义的。
为什么bar1
和bar2
的构建有效?
很明显,bar4
的构造是有效的,因为我对所有临时Foo
使用了{} -brackets,因此我明确了我想要的内容。
如果只需要使用{} -brackets只有一个Foo
来解决问题...为什么bar3
的构造失败?
此外,在构造任何Bar之前声明x。为什么编译器不抱怨?
最后一个问题与我最后一行示例代码有关。长话短说:编译器认为我希望他做什么,我在哪里想念阴影的外观?
PS:如果感兴趣的话 - 我使用gcc-4.9.2
PPS:我尝试使用bar
的构造函数将三个Foo0
作为参数。同样的故事在这里但是错误没有说明有关冲突的声明,而是关于重新定义x
。
答案 0 :(得分:10)
规则是如果声明具有函数声明的语法,那么它就是一个;否则它是一个变量声明。令人惊讶的例子有时被称为most-vexing-parse。
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
是返回类型,参数类型是Foo0
,Foo1
和Foo2
。参数名称都是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
类型的对象bar2
,bar4
和Bar
。它们不能被解析为函数声明,因为{ }
表示法在函数声明中不是有效语法。
因此,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
x
是int
;它没有任何方法。
答案 1 :(得分:2)
1)在这种情况下,Foo0(x)被视为函数bar0的参数。在这里,它是否具有标准构造函数并不重要。它不是局部变量声明和初始化,而只是函数声明中的参数声明。
2)我的猜测是这与解析有关,但如果我错了,有人会纠正我。例子bar1和bar2工作,因为编译器知道bar1和bar2是局部变量声明(而不是函数)声明一见到{}的第一次出现。这些第一次出现的{}出现在x被声明为函数的两个参数之前。
3)bar3的构造失败,因为编译器首先假设bar3是函数声明。函数声明有三个参数,都是x。显然,这是不正确的。
4)函数声明中的x只是参数的名称。它与您之前声明的整数x的范围不同。