指向基虚函数的指针与直接调用基虚函数

Pointer to base virtual function vs direct call of base virtual function

为什么这两个行为不同?

(obj.*lpfn)();

obj.Base::fn();

我知道当我们直接调用虚拟成员函数 (obj.fn()) 时,它会执行虚拟分派(为 obj 类型找到正确的 'fn' 实现,然后调用它)。但是为什么指向成员函数调用的指针设置为 Base implementation((obj.*lpfn)()) 会导致虚拟分派,为什么同样的表达式,除了直接调用 (obj.Base::fn( )) 不做虚拟派遣?如果有的话,我希望他们都调用 Print 函数的基本实现(无虚拟化)

#include <iostream>
class Base {
public:
    virtual void Print() { std::cout << "Base::Print();\n"; }
};
class Derived : public Base {
public:
    void Print() override { std::cout << "Derived::Print();\n"; }
};
int main() {
    Derived d;
    auto lpfn = &Base::Print;
    (d.*lpfn)();
    d.Base::Print();
}

输出:

Derived::Print();
Base::Print();

But why does a pointer to member function call that is set to Base implementation((obj.*lpfn)()) lead to virtual dispatch

因为语言就是这样指定的。 Virtual dispatch 是一个有用的工具,因此能够通过指向成员函数的指针使用 virtual dispatch 也很有用。

why does the same expression, except directly called (obj.Base::fn()) not do virtual dispatch?

因为偶尔使用静态调度很有用。该语言的设计方式是可以通过使用限定名称指定静态类型来完成静态分派。

语言中没有通过指向虚拟成员函数的指针使用静态分派的语法。我不能告诉你为什么会这样,但我可以猜测,鉴于虚拟函数的静态分派是很少需要的特性,而指向成员函数的指针是很少需要的特性,可能是因为它们的组合很少需要它没有真正考虑过。

来自 C++ 标准:

[expr.call]/1 For a call to a non-static member function, the postfix expression shall be an implicit (12.2.2, 12.2.3) or explicit class member access (8.2.5) whose id-expression is a function member name, or a pointer-to-member expression (8.5) selecting a function member; the call is as a member of the class object referred to by the object expression... If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider (13.3) in the dynamic type of the object expression is called; such a call is referred to as a virtual function call.

因此,只有两种情况下 non-static 成员函数的调用不执行虚拟分派:1) 函数一开始就不是虚拟的,或者 2) 函数被命名为限定名称,如 obj.Base::fn()。在所有其他情况下 - 包括通过 pointer-to-member 调用的情况 - 调用最终覆盖程序。