【问题标题】:Construction of temporary in function call is interpreted as declaration函数调用中的临时构造被解释为声明
【发布时间】:2015-03-18 01:19:52
【问题描述】:

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

#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 的声明。这是有道理的,而且完全没问题,因为您可以使用 {} 括号来明确构造。但我不明白的是:

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

  2. 为什么bar1bar2 的构造有效? 对我来说很明显bar4 的构造是有效的,因为我对所有临时的Foos 使用了{}-括号,因此我很明确我想要什么。

  3. 如果只需要使用带有Foos之一的{}-括号来解决问题......为什么bar3的构造会失败?

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

最后一个问题与我的最后一行示例代码有关。长话短说:编译器认为我希望他做什么,我在哪里错过了阴影的出现?

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

【问题讨论】:

  • 你知道,bar0 是一个函数的声明,它接受三个Foos 并返回一个Foo,它本身不是一个Foo 对象。此外,bar3 的声明不起作用这一事实是您使用的任何编译器中的一个错误。该行应该像在clang 中一样工作。
  • @0x499602D2 在 g++ 4.8.1 中为我窃听
  • 正确地说:bar0 是一个函数的声明,它接受三个 Foos 并返回一个 Bar

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


【解决方案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’

这是一个函数声明: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

这些行创建bar1bar2bar4 类型为Bar 的对象。它们不能被解析为函数声明,因为{ } 符号在函数声明中不是有效的语法。

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

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 ;它没有任何方法。

【讨论】:

  • bar0如何是合法的函数声明? type(name) 在这种情况下是什么意思?它只是绑定/分组,比如int(*foo)——所以type(name)type name一样?有时我真的开始喜欢 C++。
  • @JasonN see here 一个很好的解释
  • 谢谢。复杂解析的真棒。 -Wvexing-parse 很有趣。悲伤的同时。
【解决方案2】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-11
    • 2019-07-28
    • 2012-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    相关资源
    最近更新 更多