Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using method from indirect parent

Consider following code:

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 this calls:

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

It is surprise for me that result of 2 and 3 is different:

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

Q: Is there a paragraph in standard that address this behaviour?

like image 946
A. Lisowski Avatar asked Aug 10 '17 10:08

A. Lisowski


People also ask

Can a parent class access child class methods?

The only unusual aspect is that, within child class method definitions, you can't directly access parent class instance variables. For example, if the parent had a height instance variable, child class method definitions wouldn't be able to access this directly.


1 Answers

The difference between 2. and 3. is that 2. uses dynamic (== virtual) dispatch (== call). Dynamic dispatch is implicitly used, when a virtual function is called (see later paragraph for exception). Therefore 2. calls the most derived override, which is std::runtime_error::what which prints the message "user_error" that was given to the constructor, as required by the post condition of the constructor:

[runtime.error]

runtime_error(const char* what_arg);

4 Effects: Constructs an object of class runtime_error.

5 Postcondition: strcmp(what(), what_arg) == 0.


Function call using a scope resolution operator does static dispatch even if the function is virtual.

[class.virtual]

15 Explicit qualification with the scope operator (5.1) suppresses the virtual call mechanism.

Therefore overrides do not matter for 3. What matters is name resolution. The using declaration is like any other member declaration in that it hides the same name that would have otherwise been resolved from a parent.

So, user_error::what hides std::runtime_error::what. And, user_error::what is defined by std::exception::what.


Now, what should this non virtually called std::exception::what return according to the standard? (annotated by me):

[exception]

7 Returns: An implementation-defined NTBS. (null terminated string)

Clearly, there is no requirement to print anything in particular, such as printing a string that was passed to a constructor of a derived class that contains this as sub object. Any string is standard compliant.


A minimal example for the behaviour, that does not involve exceptions:

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

The output of the two lines must be different.

like image 111
eerorika Avatar answered Sep 29 '22 06:09

eerorika