使用间接父方法

时间:2017-08-10 10:41:53

标签: c++ language-lawyer using-declaration

请考虑以下代码:

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();
  }
};

通过这个电话:

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";

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

1. direct result: thrown
2. sliced result: user_error
3. ranged result: std::exception

问:标准中是否有解决此问题的段落?

1 个答案:

答案 0 :(得分:4)

2.和3.之间的区别在于2.使用动态(==虚拟)调度(==调用)。当调用虚函数时,隐式使用动态调度(请参阅后面的段落以了解异常)。因此2.调用最派生的覆盖,即std::runtime_error::what,它打印给构造函数的消息"user_error",这是构造函数的post条件所要求的:

  

[runtime.error]

     

runtime_error(const char* what_arg);

     

4    效果:构造类runtime_error的对象。

     

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

即使函数是虚函数,使用范围解析运算符的函数调用也会执行静态调度。

  

[class.virtual]

     

15范围运算符(5.1)的显式限定禁止虚拟调用机制。

因此,覆盖与3无关。重要的是名称解析。 using声明就像任何其他成员声明一样,它隐藏与从父进程中解析的相同名称。

因此,user_error::what隐藏std::runtime_error::what。并且user_error::whatstd::exception::what定义。

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

  

[例外]

     

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

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

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

#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;
}

两条线的输出必须不同。