使用间接父方法

A. *_*ski 6 c++ using-declaration language-lawyer

考虑以下代码:

class user_error : public std::runtime_error
{
public:
    using std::exception::what;
    explicit user_error(const std::string& what_arg):std::runtime_error(what_arg){}
};


class with_overriden_what : public user_error {
public:
  with_overriden_what(const std::string& val) : user_error("user_error"), message(val) { }

  std::string message;

  virtual const char* what() const noexcept {
    return message.c_str();
  }
};
Run Code Online (Sandbox Code Playgroud)

通过这个电话:

with_overriden_what ex("thrown");
std::cout << "1. direct result: " << ex.what() << "\n";
std::cout << "2. sliced result: " << static_cast<user_error>(ex).what() << "\n";
std::cout << "3. ranged result: " << ex.user_error::what() << "\n";
Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,2和3的结果是不同的:

1. direct result: thrown
2. sliced result: user_error
3. ranged result: std::exception
Run Code Online (Sandbox Code Playgroud)

问:标准中是否有一个段落可以解决这种问题?

eer*_*ika 4

2.和3.的区别在于2.使用动态(==虚拟)调度(==调用)。当调用虚函数时,隐式使用动态调度(有关例外情况,请参阅后面的段落)。因此 2. 调用最派生的重写,它根据构造函数的后置条件的要求std::runtime_error::what打印提供给构造函数的消息:"user_error"

[运行时错误]

runtime_error(const char* what_arg);

4 作用:构造一个runtime_error类的对象。

5 后置条件:strcmp(what(), what_arg) == 0.


使用作用域解析运算符的函数调用会进行静态调度,即使该函数是虚拟的也是如此。

[类.虚拟]

15 作用域运算符 (5.1) 的显式限定会抑制虚拟调用机制。

因此,覆盖对于 3 来说并不重要。重要的是名称解析。using 声明与任何其他成员声明一样,它隐藏了本来可以从父级解析的相同名称。

所以,user_error::what隐藏std::runtime_error::what。并且,user_error::what由 定义std::exception::what


std::exception::what现在,根据标准,这个非虚拟调用应该返回什么呢?(由我注释)

[例外]

7 返回:实现定义的 NTBS。(以空字符结尾的字符串)

显然,不需要打印任何特定内容,例如打印传递给包含 this 作为子对象的派生类的构造函数的字符串。任何字符串都符合标准。


该行为的最小示例,不涉及异常:

#include <iostream>

struct A {
    virtual void x() {
        std::cout << "A\n";
    }
};

struct B : A {
    void x() {
        std::cout << "B\n";
    }
};

struct C : B {
    using A::x;
};

int main() {
    C c;
    c.x();
    c.C::x();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

两条线的输出必须不同。