在3种主要的C ++编译器中,程序进行了不同的编译。哪一个是对的?


116

作为对我之前的问题的有趣的跟进(尽管不是很重要), 为什么在声明变量时C ++为什么允许我们在括号中将变量名括起来?

我发现将括号中的声明与注入的类名功能结合使用可能会导致有关编译器行为的令人惊讶的结果。

看一下以下程序:

#include <iostream>
struct B
{
};

struct C
{
  C (){ std::cout << "C" << '\n'; }
  C (B *) { std::cout << "C (B *)" << '\n';}
};

B *y = nullptr;
int main()
{
  C::C (y);
}
  1. 使用g ++ 4.9.2进行编译会给我以下编译错误:

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
  2. 它已成功与MSVC2013 / 2015一起编译并打印 C (B *)

  3. 使用clang 3.5成功编译并打印 C

那么强制性的问题是哪个是对的?:)

(尽管我强烈地转向了clang版本,但是从技术上仅用其typedef更改类型后,msvc停止声明变量的方法似乎有点奇怪)


3
C::C y;没道理吧?C::C (y); 最初我都不认为这是Most-Vexing-Parse stackoverflow.com/questions/tagged/most-vexing-parse的一个实例,但是现在我认为这只是未定义的行为,意味着所有三个编译器都是“正确的”。
戴尔·威尔逊

4
#3 clang绝对是错误的,#2 msvc太宽松了,#1 g ++是正确的((我猜)

8
C::C没有命名类型,而是命名函数,所以GCC是正确的imo。
Galik


Answers:


91

至少根据C ++ 11查找规则,GCC是正确的。3.4.3.1 [class.qual] / 2指定,如果嵌套名称说明符与类名称相同,则它引用构造函数而不是注入的类名称。它给出了示例:

B::A ba;           // object of type A
A::A a;            // error, A::A is not a type name
struct A::A a2;    // object of type A

看起来MSVC将其误解为函数样式的强制转换表达式Cy并使用构造函数参数创建了一个临时变量;Clang将其错误地解释为声明y为type 的变量的声明C


2
是的,关键是3.4.3.1/2。做得好!
Lightness Races in Orbit

它说:“在不忽略函数名称的查找中”。在我看来,在给出的示例中,尤其是A::A a;函数名称应该被忽略-还是不应该?
哥伦布2015年

1
按照N4296中的编号,键实际上是3.4.3.1/2.1:“如果在C中查找嵌套名称说明符之后指定的名称,则为C [...]的注入类名称该名称被认为是为C类的构造函数命名。” Mike的摘要虽然有点过分简化了-例如,在类内部的类名的typedef将允许与类名不同的嵌套名称说明符仍引用类名,因此仍将引用类名。 ctor。
杰里·科芬

2
@Mgetz:从以下问题开始:“它已成功用MSVC2013 / 2015编译并打印C (B *)
Lightness Races in Orbit

2
为了完整起见,这应阐明是需要诊断的病态还是不需要诊断的病态。如果是后者,则所有编译器都是“正确的”。
MM

16

G ++是正确的,因为它会给出错误。因为没有new运算符就无法以这种格式直接调用构造函数。尽管您的代码调用C::C,但它看起来像构造函数调用。但是,根据C ++ 11标准3.4.3.1,这不是合法的函数调用或类型名称(请参见Mike Seymour的答案)。

Clang是错误的,因为它甚至没有调用正确的函数。

MSVC是合理的,但仍未遵循该标准。


2
这是什么new操作的变化?
尼尔·柯克

1
@NeilKirk:对于那些认为这new B(1,2,3)是某种“直接构造函数调用”(当然不是这样)的人,它们与临时实例化B(1,2,3)或声明是不同的B b(1,2,3)
Lightness Races in Orbit

@LightningRacisinObrit您如何描述什么new B(1,2,3)
user2030677

1
@ user2030677:一个新表达式,使用关键字new,类型名称和构造函数参数列表。它仍然不是“直接构造函数调用”。
Lightness Races in Orbit

“ Clang是错误的,因为它甚至没有调用正确的函数。”:我认为(因为OP关于声明中括号的说明)Clang解释C::C (y);C::C y;,即,定义类型C的变量y(使用注入的类型C: :C,而错误地忽略了日益疯狂的语言规范3.4.1,2,这使C :: C成为构造函数)。imo,这并不是您似乎想像的明显错误。
彼得-恢复莫妮卡
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.