[英]Why does calling calling a pure virtual method without body does not result in linker error?
I've come across quite weird scenario today. 我今天遇到了很奇怪的情况。 When directly calling a pure virtual method in Interface constructor, I get a undefined reference error. 在接口构造函数中直接调用纯虚方法时,出现未定义的引用错误。
class Interface
{
public:
virtual void fun() const = 0;
Interface(){ fun(); }
};
class A : public Interface
{
public:
void fun() const override {};
};
int main()
{
A a;
}
Results in: 结果是:
prog.cc: In constructor 'Interface::Interface()':
prog.cc:5:22: warning: pure virtual 'virtual void Interface::fun() const' called from constructor
5 | Interface(){ fun(); }
| ^
/tmp/ccWMVIWG.o: In function `main':
prog.cc:(.text.startup+0x13): undefined reference to `Interface::fun() const'
collect2: error: ld returned 1 exit status
However, wrapping a call to fun() in a different method like this: 但是,将对fun()的调用包装在不同的方法中,如下所示:
class Interface
{
public:
virtual void fun() const = 0;
Interface(){ callfun(); }
virtual void callfun()
{
fun();
}
};
class A : public Interface
{
public:
void fun() const override {};
};
int main()
{
A a;
}
Compiles just fine and (obviously) crashes with pure virtual call error. 编译就很好,并且(显然)由于纯虚拟调用错误而崩溃。 I've tested it on latest GCC 8.2.0 and 9.0.0 and Clang 8.0.0. 我已经在最新的GCC 8.2.0和9.0.0和Clang 8.0.0上进行了测试。 Out of those, only GCC produces a linker error in the first case. 在这些情况中,只有GCC在第一种情况下会产生链接器错误。
Wandbox links for a full working example with the error: Wandbox链接显示了完整的工作示例,并显示以下错误:
https://wandbox.org/permlink/KhXsBeoRXf9v0iJr https://wandbox.org/permlink/KhXsBeoRXf9v0iJr
https://wandbox.org/permlink/38JEGGyA3hfAfPAS https://wandbox.org/permlink/38JEGGyA3hfAfPAS
EDIT: I'm getting flagged for duplication, but I'm not sure how this question is duplicated. 编辑:我被标记为重复,但我不确定如何重复此问题。 It doesn't have anything to do with dangers of calling pure virtual method (from constructor or whatnot), I'm aware of them. 我知道它们与调用纯虚拟方法(从构造函数或其他方法)的危险没有任何关系。
I was trying to understand why the compiler permits this call in one scenario, and fails to do so in another, which was explained very well by Adam Nevraumont. 我试图理解为什么编译器在一种情况下允许此调用,而在另一种情况下却不允许这样做,Adam Nevraumont对此进行了很好的解释。
EDIT2: It seems, that even if callFun
is not virtual, it still somehow prevents GCC from devirtualizing and inlining fun
call. EDIT2:看来,即使callFun
不是虚拟的,它仍会以某种方式阻止GCC取消虚拟化和内联fun
呼叫。 See the example below: 请参阅以下示例:
class Interface
{
public:
virtual void fun() const = 0;
Interface(){ callfun(); }
void callfun()
{
fun();
}
};
class A : public Interface
{
public:
void fun() const override {};
};
int main()
{
A a;
}
You aren't calling the pure virtual function, you are doing a lookup in the vtable for the current entry in the virtual function table for that function. 您不是在调用纯虚函数,而是在vtable中对该函数的虚函数表中的当前条目进行查找。
As it happens, at that point it is a pure virtual function, so you crash due to UB. 碰巧的是,那时它只是一个纯虚函数,因此您由于UB而崩溃。
In the first case, you are getting a linker error because gcc is devirtualizing the call to fun
in the ctor. 在第一种情况下,您会收到链接器错误,因为gcc正在取消ctor中对fun
的调用的虚拟化。 A devirtualized call to fun
directly calls the pure virtual method. 去fun
的非虚拟化调用直接调用纯虚拟方法。 This is possible because while constructing Interface
, the compiler knows the state of the virtual function table (derived class modifications to it do not occur yet). 这是可能的,因为在构造Interface
,编译器会知道虚拟功能表的状态(尚未对其进行派生的类修改)。
In the second case, the compiler can devirtualize the call to callFun
from the ctor. 在第二种情况下,编译器可以取消ctor对callFun
的调用的callFun
。 But the call to fun
from within callFun
cannot be devirtualized, as callFun
could be called from outside the ctor in another method. 但是从callFun
内部进行的对fun
的调用callFun
虚拟化,因为callFun
可以通过ctor之外的其他方法进行调用。 Devirtualizing it would be incorrect in the general case . 在一般情况下 ,将其去虚拟化是不正确的 。
In this specific case, if the compiler devirtualized callFun
and then inlined it, it could then devirtualize fun
in the inlined copy. 在这种特定情况下,如果编译器先对callFun进行callFun
,然后对其进行内联,则可以对内联副本中的fun
进行虚拟化。 But the compiler doesn't do this, so no devirtualization occurs. 但是编译器不会执行此操作,因此不会发生虚拟化。
As an aside, you can implement that pure virtual function and cause every example you provided to both link and run fine. 顺便说一句,您可以实现该纯虚函数,并使您提供的每个示例都链接并正常运行。
void Interface::fun() const {}
anywhere in any .cpp
file linked in will make your code link, and be correct regardless. 链接到的任何.cpp
文件中的任何位置都将使您的代码链接,并且无论如何都是正确的。 Pure virtual doesn't mean "has no implementation" in C++, it just means "derived class must provide an override, and it is legal for me not to have an implementation". 纯虚拟语言在C ++中并不表示“没有实现”,而只是表示“派生的类必须提供重写,并且对我而言,没有实现是合法的”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.