c++oopoverridingoverloadingname-hiding

Why a method of parent of parent classes' not accessible without of explicit scope resolution?


Let's consider this code:

struct message
{
  uint8_t* data;
  size_t length;
};

class device_base
{
  // ...
public:
  virtual ssize_t exec(uint8_t cmd, const uint8_t* data = nullptr, size_t length = 0);
  inline ssize_t exec(uint8_t cmd, const message& msg)
  {
    return exec(cmd, msg.data, msg.length);
  }
  // ...
};

class device : public device_base
{
  // The exec method do not overloaded or overridden here.
};

class device_uart : public device
{
  // ...
public:
  ssize_t exec(uint8_t cmd, const uint8_t* data = nullptr, size_t length = 0);
  void some_method(const message&);
  // ...
};

// ...

void device_uart::some_method(const message& msg)
{
  // exec(SOME_COMMAND, msg); // The inline method device_base::exec is invisible here by some reason.
  device::exec(SOME_COMMAND, msg); // OK.
  device_base::exec(SOME_COMMAND, msg); // OK too.
  exec(SOME_COMMAND, msg.data, msg.length); // OK, of course.
}

Why the inline non-virtual method exec is not seen in the device_uart class?


Solution

  • Why the inline non-virtual method exec is not seen in the device_uart class?

    This is a kind of "name-hiding"; in the member function of class device_uart, device_base::exec is hidden because there's a method with same name exec in the class device_uart itself. Functions can't be overloaded through different scopes.

    According to the rule of unqualified name lookup:

    name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.

    That means the name exec will be found at the scope of device_uart, then name lookup stops, the names in the base class won't be considered for overload resolution at all.

    To solve the issue, you can use scope resolution operator :: to make it qualified name lookup as you've shown; or you can use using to introduce the names into the same scope, the overload resolution would take effect as expected. e.g.

    class device_uart : public device
    {
      // ...
      using device_base::exec; // introduce the names from base class into the same scope
    public:
      // ...
    };