为什么我不能使用`= delete;`声明纯虚函数?

9

介绍

纯虚函数的声明通常采用以下语法:

virtual f() = 0; 

然而,自从C++11以来,有一种方法可以明确地传达特定成员函数的不存在

Mystruct() = delete; // eg default constructor

问题

为什么不将这种语法扩展到纯虚函数,以实现在通信此类操作时的一致性呢?:

virtual f() = delete; 

注意

我知道显而易见的答案是“因为标准这样规定!”。但我想知道背后的原因和是否曾经有类似的提议(或意图)。


3
virtual ~MyStruct() = delete; 已经被占用并且具有非常不同的功能。 - chris
@chris 是的,很有道理。即使有意愿,也无法实现。 - Nikos Athanasiou
1
= delete 已经允许应用于非特殊成员函数(包括虚函数)和非成员函数,表示“调用此函数会在编译时发生错误”。 - Oktalist
1
请记住,语法virtual f() = 0也没有任何理由。根据Bjarne Stroustrup的说法,他当时认为很难让新关键字被接受。 - Nasser Al-Shawwa
严格来说,“virtual f() = 0”实际上无法编译,因为我们不能有一个纯虚构造函数。它必须在某个地方有一个类型,否则它就不是构造函数了。 - Edward
4个回答

13

大致上说,区别在于这个:

virtual void f() = 0; 

说:“这个类是抽象的,我可能没有编写该成员函数的实现”(尽管你可以这样做)。

然而,这个:

void f() = delete;

说:“这个成员函数确实不存在,任何人都不能通过。”


1
我从未使用过delete符号,我很好奇它相比于不定义一个函数有什么优势。你有什么建议吗? - Logicrat
2
@Logicrat:这对于复制构造函数等非常有用。如果您不声明复制构造函数,则在某些/许多情况下,会为您声明一个。您必须将其标记为“private”以防止其使用,然后编译错误并不是非常清晰。=delete可以记录意图并改善诊断。 - Lightness Races in Orbit
有没有情况下我会在常规函数上使用 delete,而不是运算符?在 这个 Godbolt 示例 中,类 A 有任何意义吗?类 C 甚至无法编译。 - Thomas Weller

10

= delete; 的含义与将函数设为纯虚函数完全不同。与被删除的函数不同,纯虚函数不会使选择它作为重载决策结果的程序非法。


不会使选择它的程序出现格式错误,因为抽象类没有实例,所以在重载决议后永远无法选择纯虚函数。 - Nikos Athanasiou
4
抽象类* a = new 派生类; a->纯虚函数(); - R. Martinho Fernandes
@R.MartinhoFernandes 我不认为重载决议会选择基类的pure_virtual_function()函数。 - 101010
4
动态分发是在运行时根据实际使用的对象进行的,而重载决议是在编译时进行的。 - Dave S
@DaveS 谢谢信息。我在这个问题上提出了一个后续问题(主要是关于“为什么显式解析函数会导致编译失败”)。 - Nikos Athanasiou
显示剩余3条评论

7

这两件事情的意义有很大的区别。大概来说,差别在于:

virtual void f() = 0; 

他说:“我可能没有编写此函数的实现。”

不过,这个:

f() = delete;

说:“注意编译器:不要创建此函数的实现。”


7
后一句话的意思更加强烈!它还表示:“如果你发现我试图使用它,惩罚我吧,因为我是个非常调皮的男孩。” - R. Martinho Fernandes
2
前者根本不是那个意思。为纯虚函数提供定义是完全有效的。请参见我的答案,了解真相。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit: 我的目的是传达一个大致的近似值,以帮助理解,而不是从标准中引用章节和经文。话虽如此,我相当喜欢 R. Martinho Fernandes 对“delete”的解释。 - Edward
2
@Edward:如果你所谓的“传达大致近似值”实际上是“传播危险且普遍存在的误解”,那么你做得很好!相反,通过讲真话来帮助理解;通过说谎话,你实际上正在_损害_理解(这应该是不言自明的)。 - Lightness Races in Orbit
@LightnessRacesinOrbit: 我会把真理留给那些认为自己拥有垄断权的人。我只提供一个答案,可能对询问问题的人有用,也可能没有。 - Edward
@Edward:我现在就告诉你,这是没有用的。他们可能会被误导以为有用。 :) - Lightness Races in Orbit

2
有几个成员函数是自动生成的:默认构造函数、析构函数、拷贝构造函数、赋值操作符,也有一些新的移动构造函数。
能够删除它们的目的是确保编译器不允许调用它们。以前的技巧是将它们声明为private并不给它们实现。
这样做很麻烦,但重要的是它并没有完全阻止调用它们,因为友元类(甚至是类本身)可能会意外调用拷贝或赋值操作符,虽然你会得到一个链接错误,但这并不理想,因为你看不到错误在哪里,而且你真正想要的是编译器错误。
将一个函数声明为带有=0的虚函数并不能防止它被调用。它在使你的类成为抽象类方面具有特殊意义,因此你不能创建一个实例,并且意味着该函数不需要实现,但它与删除函数的含义不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接