【问题标题】:using method from indirect parent使用间接父母的方法
【发布时间】:2018-01-18 13:41:14
【问题描述】:

考虑以下代码:

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

问:标准中有一个段落可以解决这种行为吗?

【问题讨论】:

  • 我在 VS 2017 中尝试过,结果不同(和预期):1. 直接结果:抛出 2. 切片结果:user_error 3. 范围结果:user_error 你使用哪个编译器?
  • 2 或 3 中的哪一个是您期望两者的输出?
  • override 添加到覆盖。
  • 问题可以简化为:ideone.com/avu745what()user_error::what() 即使在 user_error 对象上调用时也会给出不同的结果。

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


【解决方案1】:

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


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

[class.virtual]

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

因此,覆盖对于 3 无关紧要。重要的是名称解析。 using 声明与任何其他成员声明一样,因为它隐藏相同的名称,否则会从父级解析。

所以,user_error::what 隐藏了 std::runtime_error::what。而且,user_error::whatstd::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;
}

两行的输出一定不同。

【讨论】:

  • 根据上面的说法,gcc (2!=3) 和visual (2==3) 的输出都可以认为是合法的,对吗?
  • 给定异常定义,提取传递给 user_error 的消息的最佳方法是什么?
  • @A.Lisowski 1) 正确。允许并且可以通过复杂的std::exception 实现打印给str::runtime_error 的值,但不是必需的。我添加了我的简化示例,其中输出必须不同 2.) ex.runtime_error::what().
  • (或者干脆删除 using 声明,这似乎适得其反)。
  • @A.Lisowski 然后改用using std::runtime_error::what
猜你喜欢
  • 1970-01-01
  • 2016-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-30
  • 2011-01-07
相关资源
最近更新 更多