【问题标题】:Why do gcc and clang allow me to construct an abstract class?为什么 gcc 和 clang 允许我构造一个抽象类?
【发布时间】:2016-08-29 06:19:47
【问题描述】:

以下代码在各种 gcc 和 clang 版本上编译 - 当使用 gcc 5.3.1 编译和运行时,它会打印

A()

然后以纯虚拟调用错误中止。

#include <stdio.h>

class A
{
public:
    A() {
        printf("A()\n");
    }
    virtual void b() const = 0;
};

int main()
{
    const A& a{};
    a.b();
    return 0;
}

我意识到将引用绑定到临时对象并不理想(尽管我认为这种情况已被某种生命周期延长所涵盖) - 但在尝试调用采用常量引用如:

Foo({});

为方便起见,这里是使用 clang 3.2 编译的示例:Compiler Explorer

【问题讨论】:

  • 允许通过指针或引用构造抽象类,记得吗?
  • @DeiDei 我不记得了,但它会解释原因。编辑:要清楚,它实际上是在构造一个对象,而不仅仅是一个引用。
  • @Soren 当您将临时对象绑定到 const 引用时,临时对象的生命周期会延长到引用的生命周期。
  • @DeiDei 我也不记得了,我在网上找到的参考页面似乎是相反的。你能暗示一下如何找到这方面的文档吗?
  • @DeiDei:为了将临时对象绑定到引用,您必须首先构造一个临时对象。这需要构造一个该类型的对象。并且您不允许构造抽象类类型的完整对象。那么在哪里说你可以构造一个A 类型的临时对象?

标签: c++ gcc clang abstract-class pure-virtual


【解决方案1】:

为什么 gcc 和 clang 允许我构造一个抽象类?

因为按照标准,它们已经坏了。

10.4 节定义了抽象类的工作方式。它包含这一行(在 C++14 中):

不能创建抽象类的对象,除非作为派生类的子对象。

带有花括号初始化列表的引用的初始化规则将构造一个临时并将其绑定到引用。临时对象是对象。因此,您在上面编写的代码将尝试创建一个“抽象类的对象”,而不是“从它派生的类的子对象”。

标准明确禁止的东西。在这方面,标准没有歧义。虽然 10.4,p3 确实指定了编译器在键入时需要彻底纠正错误的位置(将抽象类声明为函数参数、显式转换等),但标准仍然要求实现禁止将抽象类构造为某种东西除了“从它派生的类的子对象”。

临时不是“从它派生的类的子对象”。因此,编译器有义务禁止这样做。

任何没有错误的编译器。

【讨论】:

  • 在我看来是正确的,该部分似乎没有任何解决办法。我现在检查了 MSVC 2015,它也简单地打印了一个错误:“'A':无法实例化抽象类”,所以至少它不是 all 编译器
  • 您忘记在回答中提到 OP 的代码调用未定义的行为恕我直言。请参阅:melpon.org/wandbox/permlink/b8w0T43wNxvzeTME
  • @Destructor 我确实提到了 OP 中的纯虚拟调用,纯虚拟调用绝对是未定义的行为。但根据规范,我认为代码不会调用 UB,因为它不允许编译。感谢您提交错误报告!
  • @Matt:是的,绝对不允许按照标准编译。
  • @Matt:clang++ 已修复此错误。观看现场演示here
猜你喜欢
  • 2018-09-10
  • 2014-11-22
  • 2019-06-09
  • 2016-12-17
  • 2019-11-20
  • 2017-12-08
  • 1970-01-01
  • 2018-02-27
  • 1970-01-01
相关资源
最近更新 更多