【问题标题】:Are virtual method not "delete"able in C++0x?虚拟方法在 C++0x 中不能“删除”吗?
【发布时间】:2011-09-21 22:46:09
【问题描述】:

当我们尝试delete virtual 方法时,错误消息似乎与场景有点误导。

prog.cpp:4:16: error: deleted function 'virtual void Test::foo()'
prog.cpp:8:2: error: used here

代码

struct Test : public Base
{
  Test() {}
  virtual void foo () = delete;  // error
};

virtual 方法不是 deleteable 是否出于同样的原因,为什么它们不能在 C++03 中保持未实现?有没有办法提到Test 故意不实现virtual foo()

【问题讨论】:

  • 尝试将它们称为虚拟而不删除。
  • 哇,我到现在还没有意识到你可以做到这一点。其次,它是否有帮助或有用?
  • @DumbCoder:嗯,你不能。你得到一个错误。因此提出了这个问题。
  • 如果我们应该被允许调用(Base*)->foo(),而Base*可以是Test*,那么我们怎么能让Test既不实现foo也不抽象呢?从必须存在的派生类中删除一个函数有什么意义?
  • @Tomalak Geret'kal - 我更想知道它有多大帮助,你会在哪里使用它?应该说清楚。可能会去看看标准文档。

标签: c++ c++11 virtual


【解决方案1】:

如果你的派生类不能实现它,也许你不应该从一个需要某些东西的类派生它。可能您一开始就不需要从该类派生。

这就像说“我正在制造一辆更好的汽车。这是一种汽车,但没有配备轮子。”你确定那是一辆车吗?

或者你只是出于好奇才问这个?

【讨论】:

  • 这应该是评论吗?
【解决方案2】:

我的理解是 delete 关键字用于删除编译器生成的默认实现。对于虚方法,编译器没有生成默认实现。

【讨论】:

  • 对于默认实现,我们有 = default 关键字。 = delete 禁止引用该成员。
  • delete 可以用来删除默认实现和禁止特定成员(其实是一回事)。
  • @iammilind:确实,delete 不允许 的东西。但是你不能在派生类中禁止基类承诺在其所有派生类中存在的东西,不是吗?
【解决方案3】:

所有非纯虚函数都必须实现,whether you use them or not:

struct Test
{
  Test() {}
  virtual void foo();
};

int main() {
   Test* t = new Test;
   // ^ it seems to have to be dynamic allocation to coerce the error out
}

/* Output:
/home/Y3oGMf/ccOLuYWf.o: In function `main':
prog.cpp:(.text+0x17): undefined reference to `vtable for Test'
collect2: ld returned 1 exit status
*/

我认为您看到关于 use 出于类似原因的已删除函数的稍微奇怪的错误消息。这只是因为事情在内部是如何运作的。在您尝试 delete 一个 virtual 函数成员的行中可能会出现一条更明智的错误消息,说“这不起作用。它会导致问题,因为这个实现必须存在。”

无论如何delete 一个接口函数是没有意义的。继承增加了功能;它不会把它拿走。请注意,将成员函数 pure 设为虚拟会如何禁止整个类被实例化:派生类必须重新实现该功能,以免丢失。

【讨论】:

  • 你有另一个角落案例。这是一个编译器错误……但不幸的是,这是一个可怕的东西。问题是:应该将 vtable 和 typeinfo 发送到哪个目标文件中? C++ 不需要单个 .cpp 文件来实现整个类。 gcc 的规则是 vtabletypeinfo 与类的第一个非内联函数在同一个目标文件中发出(这里是 foo,因为构造函数是内联的),并且未能定义此函数会导致这个奇怪的链接器错误......见ideone.com/rEp41(只需在foo之前添加另一个非内联方法)
  • @Matthieu:我不明白为什么这是一个极端情况或编译器错误。你写的理由在我看来完全合理且正常
  • 该标准没有指定何时发出信息,我们可以争辩说,无论是否存在其他“错误”,符合标准的编译器都应该始终发出它。第一次收到错误消息时肯定会感到困惑!但从标准的角度来看,程序在第一个错误时就失去了意义,所以讨论没有实际意义。
【解决方案4】:

术语use在标准中有一个具体的定义,特别是对于虚函数,odr-used的定义是:

§3.2/2 (C++0x FDIS) [...]如果它不是纯的,则虚拟成员函数是 odr-used。[...]

odr-used 是即将发布的标准中的一个新术语,指的是以前的标准简单地称为 used

§3.2/2(当前标准)[...]如果它不是纯的,则使用虚拟成员函数。[...]

我的看法是,在这种特殊情况下,错误消息使用术语 used 来指代 odr-used,是的,这是违规的原因是与您不能不实现非纯虚成员函数的原因完全相同。

【讨论】:

  • 那么,为同一个函数同时指定deletevirtual 有意义吗?在我看来,任何一方都应该“获胜”,而delete 获胜似乎并不明智......
  • @Matthieu M.:我猜该标准可以明确声明 delete 不能与 virtual 方法一起使用,但话又说回来,它确实是这样 say通过整套规则...编译器应该能够警告您该特定组合将永远不会起作用,并且可能在将来它们会起作用,到目前为止,您会得到一个稍微神秘的错误
  • 我会在 CLang 上测试一下,希望对诊断更有帮助。
【解决方案5】:

允许删除虚函数,但不允许覆盖它们。

§10.3(11) 说:

在类中声明的虚函数应被定义,或在该类中声明为纯(10.4),或两者兼而有之;但不需要诊断 (3.2)。

§8.4.1 说定义了一个已删除的函数

§10.3(16) 说:

具有已删除定义的函数 (8.4) 不应覆盖没有已删除定义的函数。同样,没有删除定义的函数也不应覆盖定义已删除的函数。

最后一条规则的目的是确保可以在编译时诊断对已删除函数的调用。

所以下面的代码格式不正确:

struct Base {
    virtual void foo();
};
struct Derived : public Base {
    virtual void foo() = delete;
};

但以下代码格式正确:

struct Base {
    virtual void foo() = delete;
};
struct Derived : public Base {
    virtual void foo() = delete;
};

DEMO

OP 有一个编译器错误或 QoI 问题(很难说是哪一个,因为他没有发布整个代码),这已得到修复。

【讨论】:

    猜你喜欢
    • 2011-04-24
    • 1970-01-01
    • 2021-11-17
    • 2013-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 2012-12-03
    相关资源
    最近更新 更多