我希望如果类的类型在编译时已知,编译器能够静态解析对虚函数的函数调用(例如,如果类实例没有通过引用或指针使用,如情况 1 所示) ) 以下)。
但是,我观察到 Visual Studio 2010 的 C++ 编译器有一个奇怪的行为,我想知道当类的实例具有虚函数是通过引用访问的结构中的成员。
我是否应该期望编译器在下面的情况 2) 中静态绑定对 f() 的调用?即使
a
是 A
而不是 A&
,cr 的“引用”性是否以某种方式传播到 cr.a?
struct A
{
virtual void f() ;
virtual ~A() ;
};
struct B : A
{
virtual void f() ;
virtual ~B() ;
};
struct C {
A a ;
B b ;
};
C & GetACRef() ;
void test()
{
// Case 1) The following calls to f() are statically bound i.e.
// f() is called without looking up the virtual function ptr.
C c ;
c.a.f() ;
c.b.f() ;
A a ;
a.f() ;
// Case 2) The following calls to f() go through the dynamic dispatching
// virtual function lookup code. You can check if you generate the .asm
// for this file.
C & cr = GetACRef() ; // Note that C is not polymorphic
cr.a.f() ; // visual C++ 2010 generates call to f using virtual dispatching
cr.b.f() ; // visual C++ 2010 generates call to f using virtual dispatching
}
显然编译器作者并没有费心去解决这个问题。也许它在实际代码中不够常见,不值得他们关注。
如果
GetACRef
在其他地方定义,也可能 C 在那里是多态的,这可能会影响优化。
请注意,编译器并没有解决所有可能的情况,即小型测试程序对人类来说是“显而易见的”。编译器关注的是大型实际程序中经常发生的情况。
我不知道为什么 MSVC 不将您的“案例 2”场景编译为直接调用 - 这当然是可能的。我想只有微软才能回答。
请注意,GCC 确实会执行您正在寻找的优化(使用 MinGW 4.5.1 和
-O2
进行测试)。
此外,MSVC 即使对于以下序列也使用 vtable 调度(为了清楚起见 - 我正在使用
/Ox
优化选项):
A a;
A& ar(a);
ar.f();
因此,不需要函数或容器结构向编译器添加潜在的混乱层 - 编译器没有理由不能将
ar.f()
与该序列中的 a.f()
完全相同。 但正如博·佩尔森(Bo Persson)所建议的那样,也许这不是一个极其常见的优化场景(或者微软只是没有抽出时间来解决它)。同样,只有 MS 的编译器开发人员才能回答。
我不确定我是否会将这种行为归类为“奇怪” - 这是一个被错过的优化机会。我不确定这种事情有多常见。 在这种情况下,您是否应该期望编译器生成静态绑定调用? 或许。 但我认为它没有发生也不足为奇。
也许应该在 MS Connect 上提出问题。