luisremis luisremis - 2 months ago 11
C++ Question

Template inheritance: There are no arguments that depend on a template parameter

I came across this error when compiling the following code.
After doing some research and reading similar errors in different situations, I came up with the solution I needed.
But I did not fully understood the undelying reason for the error and the fix.

template <typename T>
class TestA {
int a;
T temp;

protected:
int b;

public:
int c;

TestA(T te): a{10}, b{20}, c{30}, temp{te} {}

int geta(){ return a; }
int getb(){ return b; }
int getc(){ return c; }
};

template <typename T>
class TestB {
int atb;
T tempb;

protected:
int btb;

public:
int ctb;

TestB(T te) atb{10}, btb{20}, ctb{30}, tempb{te} {}
};

template <typename T>
class TestInh : public TestA<T>, public TestB<T> {
int aa;
T temptemp;

protected:
int bb;
int b;

public:
int cc;

TestInh(T te) : TestA<T>{te}, TestB<T>{te}, bb{10000}, b{-1000} {}

int get_total() {
// The error happens here!
return geta();
}
};

int main(int argc, char const *argv[]) {
char text = 'a';
TestInh<char> test(text);

//std::cout << test.geta() << std::endl;
std::cout << test.get_total() << std::endl;
//std::cout << test.c << std::endl;
return 0;
}


When compiling this code, I got this error:

testtemplate.cc: In member function ‘int TestInh<T>::get_total()’:
testtemplate.cc:54:32: error: there are no arguments to ‘geta’ that depend on a template parameter, so a declaration of ‘geta’ must be available [-fpermissive]
int get_total() {return geta();}
^
testtemplate.cc:54:32: note: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)


It is solved by calling
this->geta()
instead of just
geta()
, but I do not fully understand why this cannot be resolved by the compiler.

Can someone please explain me why?

Answer

When extending a class that depends on a template parameter, this kind of become a dependent name.

The problem is that while performing two phase name lookup, the compiler can't know where he can find the function geta. He cannot know it comes from the parent. Because template specialization is a thing, TestA<int> and TestA<double> could be two completely different clas swith different functions and members.

With the this keyword added, the compiler know that geta must be a member function.

Without that, it could be either a member function or non-member function, or a member function of TestB.

Imagine a template code that will either call a function geta from TestA and geta from TestB depending on some template conditions. Ouch. The compiler want to be sure that the code is consistent for every template instantiations.

Another way of saying to the compiler that the function exist as a member function is to add a using statement:

template <typename T> 
struct TestInh : TestA<T>, TestB<T> {
    // some code...

    using TestA<T>::geta;

    int get_total() {
        // works! With the above using statement,
        // the compiler knows that 'geta()' is
        // a member function of TestA<T>!
        return geta();
    }
};