Inheritance and templates in C++ - why are inherited members invisible?
Asked Answered
T

5

24

When a template publicly inherits from another template, aren't the base public methods supposed to be accessible?

template <int a>
class Test {
public:
    Test() {}
    int MyMethod1() { return a; }
};

template <int b>
class Another : public Test<b>
{
public:
    Another() {}
    void MyMethod2() {
        MyMethod1();
    }
};

int main()
{
    Another<5> a;
    a.MyMethod1();
    a.MyMethod2();
}

Well, GCC craps out on this... I must be missing something totally obvious (brain melt). Help?

Tinker answered 14/10, 2009 at 17:17 Comment(6)
You missed a semicolon after your second class definition.Silvertongued
You are missing a ; at the end of class Another. If you fix that, then it is compiling fine in VS2008Tundra
@Naveen: That VC accepts this is due it not implementing proper two-phase lookup.Enesco
@OldCoder: Please try to post code that has only the problem you're interested in. Otherwise you'll get many answers pointing out many (other) problems, too.Enesco
@OldCoder: When you're asking a question about an error, you should post the error message.Froude
This happens because of the two-phase name lookup (which not all compilers use by default). There are 4 solutions to this problem: 1) Use the prefix Test<a>::MyMethod1, 2) Use the prefix this->MyMethod1, 3) Add a statement using Test<a>::MyMethod1, 4) Use a global compiler switch that enables the permissive mode. The pros & cons and details of these solutions are described in #50322288Deciare
S
32

This is part of the rules concerning dependent names. Method1 is not a dependent name in the scope of Method2. So the compiler doesn't look it up in dependent base classes.

There two ways to fix that: Using this or specifying the base type. More details on this very recent post or at the C++ FAQ. Also notice that you missed the public keyword and a semi-colon. Here's a fixed version of your code.


template <int a>
class Test {
public:
    Test() {}
    int MyMethod1() { return a; }
};

template <int b>
class Another : public Test<b>
{
public:
    Another() {}
    void MyMethod2() {
        Test<b>::MyMethod1();
    }
};

int main()
{
    Another<5> a;
    a.MyMethod1();
    a.MyMethod2();
}

Schott answered 14/10, 2009 at 17:25 Comment(5)
Yep, that's the correct answer. (Note that, TTBOMK, this->MyMethod1() should also work.)Enesco
If you think it's insane that this is necessary, you are not alone. Still, it is a sad fact of life.Was
+1. Also worth reading is the C++ Template FAQ: womble.decadentplace.org.uk/c++/template-faq.html very nice!Cyclohexane
That's a nice one, litb. I didn't know Ben's site.Enesco
It is also possible to just declare using Test<b>::MyMethod1() somewhere in class Another.Nihil
L
14

You should fully qualify MyMethod1. C++ Standard clearly states this in 14.6.2/3:

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

So, you should write:

void MyMethod2() {
    Test<b>::MyMethod1();
}
Leaper answered 14/10, 2009 at 17:43 Comment(1)
And if you are wondering "why the stupid restriction when base class of the class template depends on a template-parameter?", see In a templated derived class, why do I need to qualify base class member names with “this->” inside a member function?Nace
H
2

main needs a return type.

class Another needs a terminating semi-colon.

class Another needs its members to be public.

Also, methods aren't generally considered invisible; the methods were inaccessible without the public access keyword.

Hall answered 14/10, 2009 at 17:23 Comment(2)
It's fine to omit a return statement in main() according to the c++ standard, in which case 0 is returned.Drama
IOW - I meant the type was missing from the decl, rather than a return value in the body of main.Hall
S
1

I cleaned up your code to this:

template <int a>
class Test {
public:
    Test() {}
    int MyMethod1() { return a; }
};

template <int b>
class Another : public Test<b>
{
public:
    Another() {}
    void MyMethod2() {
        MyMethod1();
    }
};


int main()
{
    Another<5> a;
    a.MyMethod1();
    a.MyMethod2();
}

And compiled with -fpermissive with no problems (you can probably resolve this issue).

Silvertongued answered 14/10, 2009 at 17:23 Comment(3)
This still has an error (it shouldn't compile in a standard conformant compiler). See my post below.Schott
MSVC will build this as long as Disable Language Extensions is off (the default).Hypocrite
That's because VC doesn't to proper two-phase lookup. Symbols are, basically, only looked up when the template is instantiated. (I think it does a little bit more, but just barely. It's amazing what kind of stupid mistakes go unnoticed without two-phase lookup until someone instantiates the template. If you wrote the template, that can be quite embarrassing.)Enesco
H
0

I think you are just missing a public: at the top of the Another definition. For questions like this it is usually helpful to post the error messages that you are getting.

Hypocrite answered 14/10, 2009 at 17:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.