【问题标题】:Program being compiled differently in 3 major C++ compilers. Which one is right?程序在 3 个主要 C++ 编译器中以不同方式编译。哪一个是对的?
【发布时间】:2015-06-23 06:09:58
【问题描述】:

作为我上一个问题的一个有趣的后续行动(尽管实际上并不重要): Why does C++ allow us to surround the variable name in parentheses when declaring a variable?

我发现将括号中的声明与injected class name 功能结合起来可能会导致关于编译器行为的令人惊讶的结果。

看看下面的程序:

#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 版本,而 msvc 在更改类型后停止声明变量的方式从技术上讲它的 typedef 似乎有点奇怪)

【问题讨论】:

标签: c++ language-lawyer


【解决方案1】:

GCC 是正确的,至少根据 C++11 查找规则。 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 的变量的声明,类型为 C

【讨论】:

  • 是的,3.4.3.1/2 是关键。干得好!
  • 它说“在不忽略函数名称的查找中”。在我看来,在给出的示例中,特别是 A::A a;,函数名称应该被忽略 - 还是不?
  • 按照 N4296 中的编号,关键是 3.4.3.1/2.1:“如果在嵌套名称说明符之后指定的名称,在 C 中查找时,是注入类- C 的名称 [...] 该名称被认为是命名 C 类的构造函数。” Mike 的总结有点过于简单了——例如,类中的类名的 typedef 将允许与类名不同的嵌套名称说明符仍然引用类名,所以它仍然引用演员。
  • @Mgetz:来自问题:“使用 MSVC2013/2015 编译成功并打印C (B *)
  • 为了完整起见,这应该澄清它是否需要诊断的格式错误,或者不需要诊断的格式错误。如果是后者,那么所有编译器都是“正确的”。
【解决方案2】:

G++ 是正确的,因为它给出了错误。因为没有new操作符就无法直接以这种格式调用构造函数。尽管您的代码调用了C::C,但它看起来像是一个构造函数调用。但是,根据 C++11 标准 3.4.3.1,这不是合法的函数调用,也不是类型名称 (see Mike Seymour's answer)。

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

MSVC 是合理的,但仍然不符合标准。

【讨论】:

  • new 运算符有什么变化?
  • @NeilKirk:很多人认为new B(1,2,3) 是某种“直接构造函数调用”(当然不是),不同于临时实例化B(1,2,3)或声明B b(1,2,3)
  • @LightningRacisinObrit 你会如何描述new B(1,2,3) 是什么?
  • @user2030677:一个新表达式,使用关键字new、类型名称和构造函数参数列表。它仍然不是“直接构造函数调用”。
  • “Clang 是错误的,因为它甚至没有调用正确的函数。”:我认为(因为 OP 关于声明中括号的评论)Clang 将 C::C (y); 解释为 C::C y;,即定义C 类型的变量 y (使用注入的 C::C 类型,同时错误地忽略了越来越疯狂的语言规范 3.4.1,2,这使得 C::C 成为构造函数)。这并不像你想象的那样是一个明显的错误,imo。
猜你喜欢
  • 2019-09-18
  • 1970-01-01
  • 1970-01-01
  • 2013-05-16
  • 2016-07-09
  • 1970-01-01
  • 1970-01-01
  • 2015-07-07
  • 2011-03-04
相关资源
最近更新 更多