Bill Greene
Bill Greene

Reputation: 153

C++ Compiler error in inherited template class

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

Upvotes: 1

Views: 183

Answers (1)

Andy Prowl
Andy Prowl

Reputation: 126432

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();
//  ^^^^^^^^^^^^^^^^^^^^^^^
};

Upvotes: 2

Related Questions