Notinlist
Notinlist

Reputation: 16640

C++: Weird behavior on method overloading

I need explanation about why the following code does not compile. I have a workaround which I will articulate below, but I don't understand the failure of the original version.

To speed up code reading: The concept is to define an interface (ISomething), then to create an abstract implementation (ASomething) which implements the second function (2) using the first (not yet defined) one (1). A complete implementation which derives from the abstract one (for example SomethingImpl) must define the first method and has the option to override the second one.

#include <iostream>

class ISomething
{
public:
  virtual ~ISomething()
  { }
  virtual int f(int x) = 0; // (1)
  virtual int f(int x, int y) = 0; // (2)
};

class ASomething
  : public virtual ISomething
{
public:
  virtual int f(int x, int y) // (2)
  {
    return f(x) + f(y); // (3)
  }
};

class SomethingImpl
  : public ASomething
{
public:
  virtual int f(int x) // (1)
  {
    return x+1;
  }
};

int main()
{
  SomethingImpl a;
  std::cout << a.f(10) << std::endl; // (1)
  std::cout << a.f(10,20) << std::endl; // (2)
  return 0;
}

Compiling this code gives error on both Visual Studio 2013 (Windows) and g++ 4.4.5 (Linux). The errors are very similar, I will detail the g++ output only:

$ g++     SibFun.cpp   -o SibFun
SibFun.cpp: In member function ‘virtual int ASomething::f(int, int)’:
SibFun.cpp:18: error: no matching function for call to ‘ASomething::f(int&)’
SibFun.cpp:16: note: candidates are: virtual int ASomething::f(int, int)
SibFun.cpp:18: error: no matching function for call to ‘ASomething::f(int&)’
SibFun.cpp:16: note: candidates are: virtual int ASomething::f(int, int)
SibFun.cpp: In function ‘int main()’:
SibFun.cpp:36: error: no matching function for call to ‘SomethingImpl::f(int, int)’
SibFun.cpp:26: note: candidates are: virtual int SomethingImpl::f(int)
make: *** [SibFun] Error 1

I tried to use different notation at (3) like return this->f(x) + this->f(y), but I experienced no significant change in the error message.

However when I changed (3) to return ISomething::f(x) + ISomething::f(y); I only got:

$ g++     SibFun.cpp   -o SibFun
SibFun.cpp: In function ‘int main()’:
SibFun.cpp:36: error: no matching function for call to ‘SomethingImpl::f(int, int)’
SibFun.cpp:26: note: candidates are: virtual int SomethingImpl::f(int)
make: *** [SibFun] Error 1

But! When change (2) from f to g all compiles and runs as expected.

What is the reason behind this begavior? Why can't I use the f name for (2)?

Upvotes: 1

Views: 115

Answers (3)

user2249683
user2249683

Reputation:

The problem is not an 'overloading' problem, but hiding a base class function (See other answers)

A slightly modified example:

#include <iostream>

class ISomething
{
    public:
    virtual ~ISomething() {}
    virtual int f(int x) = 0; // (1)
    virtual int f(int x, int y) = 0; // (2)
};

class ASomething: public virtual ISomething
{
    public:
    virtual int f(int x, int y) // (2)
    {
        // `this->f(x)` fails: 
        ISomething& i = *this;
        return i.f(x) + i.f(y); // (3)
    }
};

class SomethingImpl: public ASomething
{
    public:
    virtual int f(int x) // (1)
    {
        return x+1;
    }
};

int main()
{
    SomethingImpl a;
    // `a.f(10, 20)` fails:
    ISomething& i = a;
    std::cout << i.f(10) << std::endl; // (1)
    std::cout << i.f(10, 20) << std::endl; // (2)
    return 0;
}

Hence, calling f from the interface, resolves the conflict. Although, you should consider using base::f, as suggested in other answers.

Upvotes: 1

Barry
Barry

Reputation: 303517

Both compilation failures happen for the same reason. While you're overriding one virtual member function, you're hiding the other. In ASomething:

virtual int f(int x, int y) // (2)
{
    return f(x) + f(y); // (3)
}

Name lookup on f finds ASomething::f and stops, it doesn't keep going to look for other overloads. Since ASomething::f takes two arguments, and you're trying to call it with one, error. In order to allow for overloading against the base class, you have to introduce the base class member functions with a using-declaration:

using ISomething::f; // NOW, ISomething::f(int ) is found by lookup

virtual int f(int x, int y)
{
    return f(x) + f(y);
}

And similarly, SomethingImpl needs a using ASomething::f; statement so that a.f(10) can compile.

Upvotes: 4

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48487

Function overloading works only for functions visible in the same scope:

class ASomething
    : public virtual ISomething
{
public:
  virtual int f(int x, int y) // (2)
  {
    return f(x) + f(y); // (3)
  }

  using ISomething::f;
  //~~~~~~~~~~~~~~~~~^
};

class SomethingImpl
   : public ASomething
{
public:
  virtual int f(int x) // (1)
  {
    return x+1;
  }    

  using ASomething::f;
  //~~~~~~~~~~~~~~~~~^
};

Upvotes: 7

Related Questions