【问题标题】:Statically chosen function and virtual function静态选择函数和虚函数
【发布时间】:2018-09-20 10:38:50
【问题描述】:

最近我看到了这个 C++ 标准段落 (http://eel.is/c++draft/expr.post#expr.call-5):

如果后缀表达式指定析构函数,则函数调用表达式的类型为 void;否则,函数调用表达式的类型是静态选择函数的返回类型(即忽略 virtual 关键字),即使实际调用的函数类型不同。此返回类型应为对象类型、引用类型或 cv void。

这部分我不是很懂:

函数调用表达式的类型是静态选择函数的返回类型(即忽略virtual关键字),即使实际调用的函数类型不同。

  1. 这里静态选择的函数是什么?
  2. 如何静态选择虚函数?我一直以为是在运行时选择的。
  3. 即使实际调用的函数类型不同。

调用表达式如何实际调用所选择的不同类型的函数?

【问题讨论】:

  • 笨拙的措辞提醒读者名称查找、重载解析和表达式类型的确定是在编译时完成的,因为 C++ 是一种静态类型语言。没有必要提醒读者 std,因为它几乎无处不在。名称查找显然不能在评估表达式的动态类型中完成。访问控制还考虑静态类型。模板也不会在运行时实例化!

标签: c++ language-lawyer virtual-functions static-typing covariant-return-types


【解决方案1】:

虚函数可以有协变返回类型,

所以

struct Base
{
    virtual ~Base() = default;
    virtual Base* Clone() const { return new Base(*this); }
};

struct Derived : Base
{
    // covariant return type:
    Derived* Clone() const override { return new Derived(*this); }
};

然后

Derived d;

Base& b = d;

auto* clonePtr = b.Clone(); // `auto` is `Base`, even if `Derived::Clone` is called.
                         // runtime type of `clonePtr` is `Derived`
std::unique_ptr<Base> clone(clonePtr); // Done in 2 steps for explanation

【讨论】:

    【解决方案2】:

    先举个例子来说明。

    struct B { 
      virtual B* f() { return this; }
    };
    
    struct D : B { 
      D* f() override { return this; }
    };
    
    void bar(B*) {}
    void bar(D*) {}
    
    int main() {
      D d;
      B& b = d;
      bar(b.f()); // calls `bar(B*)`
    }
    

    这里,后缀表达式b.f 表示一个函数。它是B::f,它的返回类型是B*。即使在覆盖 f 时,指定的返回类型是协变的 (D*)。实际调用(假设)在运行时解决的事实不会改变我们静态选择函数的 identity 的事实。当还涉及超载时,这是相关的。相同的函数名称可以指定两个或多个函数,并且(静态地)选择要调用的重载是重载决议。该重载可能会在派生类中被覆盖,但它的标识仍然是静态的。

    【讨论】:

    • 我选择了这个作为接受,因为它实际上有助于理解statically chosen function 的含义。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    相关资源
    最近更新 更多