1

I'm getting a C++ compiler error that I don't understand and haven't been able to find a fix or an explanation for. Here is a code snippet demonstrating the problem.

#include <iostream>

template<class T>
class A {
public:
  A(int n) {data = new T[n]; }
  const T &operator()(int i) const {return data[i];}
protected:
  T *data;
};

template<class T>
class B : public A<T> {
public:
  B(int n) : A<T>(n) {}
  T &operator()(int i) {return this->data[i]; }
  //const T &operator()(int i) const {return this->data[i];} // fixes problem
};

template<class T, int N>
class C : public B<T> {
public:
  C() : B<T>(N) {}
private:
};

template<class T>
void doOp(const T &v) {
  std::cout << v(0) << std::endl;
}

void templateTest()
{
  C<double, 3> c;
  c(0) = 5;
  std::cout << c(0) << std::endl;

  doOp(c);
}

If I un-comment the line in class B, the code compiles and executes correctly but I don't understand why defining this operator function in class B is any different from the definition in class A.

Thanks for the help.

Bill

1
  • 2
    and what is the error message? don't you think it might be relevant here? Commented Apr 28, 2013 at 13:12

1 Answer 1

2

The problem is that doOp() is invoking a non-const member function through a reference to const.

If you uncomment the commented line, a viable const member function will be found, and overload resolution will pick that version of the call operator.

Without uncommenting that line, the inherited version of the call operator is not found because it is being hidden by the overloaded call operator in the subclass.

To illustrate the problem of name hiding with a simpler example, consider the following program:

struct X
{
    void foo() { }
};

struct Y : X
{
    void foo(int) { }
};

int main()
{
    Y y;
    y.foo(42); // OK
    y.foo(); // ERROR! Name hiding...
}

The compiler here won't be able to resolve the call to y.foo(), because X::foo() is being hidden by Y::foo() here.

To fix the problem, you could add a using declaration in Y:

struct X
{
    void foo() { }
};

struct Y : X
{
    using X::foo;
//  ^^^^^^^^^^^^^

    void foo(int) { }
};

int main()
{
    Y y;
    y.foo(42); // OK
    y.foo(); // OK
}

Now both function calls are correctly resolved. In your program, you could add a similar using declaration for operator () of the base class:

template<class T>
class B : public A<T> {
public:
    B(int n) : A<T>(n) {}
    T &operator()(int i) {return this->data[i]; }

    using A<T>::operator();
//  ^^^^^^^^^^^^^^^^^^^^^^^
};
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks very much for the simple example and the fix! I had obviously forgotten about overloaded functions in base classes being hidden. But after you pointed it out to me, I see this behavior is clearly explained in e.g. Lippman's book. ;-( Thanks again for the help. Bill

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.