This question already has answers here:
template base class typedef members invisible
(1 answer)
Why do I have to access template base class members through the this pointer?
(3 answers)
Closed 2 years ago.
I have the following code which works fine:
template<typename T>
struct Foo
{
using Bar = int;
};
struct Baz : public Foo<Baz>
{
Bar bar;
};
Now I want to change Baz to accept a template parameter itself.
template<typename T>
struct Foo
{
using Bar = int;
};
template<typename T>
struct Baz : public Foo<Baz<T>>
{
Bar bar;
};
This fails to compile in every compiler with error: unknown type name 'Bar'. In fact, even if I put using Foo<Baz>::Bar; above that line, it still fails. (Yet the using itself compiles file!) Why?
This question already has answers here:
Why doesn't a derived template class have access to a base template class' identifiers?
(4 answers)
Closed 7 years ago.
The following code
template<int c>
struct Base
{
static const int a = c + 5;
};
template<int c>
struct Derived : Base<c>
{
static const int b = a + 5;
};
... fails to compile because a was not declared in this scope. Explicitly specifying Base<c>::a works, but logically this shouldn't be necessary because we're deriving from Base<c>. Is this intended behaviour (and why) or am I missing something?
template<int c>
struct Derived : Base<c>
{
static const int b = Base<c>::a + 5;
};
It really depends on compiler, but gcc requires it, it plays stupid when it comes to template classes
I have for following Situation:
template <class K> class A
{
public:
int a;
};
class B
{
public:
virtual void DoSomething() = 0;
};
template <class K> class C : public A<K>, public B
{
public:
virtual void DoSomething()
{
a = 3; // ***
}
};
Now this works on MSVC, however gcc tells me for the line with the 3 stars : "error: 'a' was not declared in this scope". I figured out that I can replace the line with
A::a = 3;
and it works on gcc (well mingw) as well. Do we have to add the original class name all the time to be standard conform? I thought, I only have to add it if names collide otherwise.
I'm using mingw32 (gcc) 4.8.1.
Now it makes sense...
GCC is right, and MSVC is wrong. It should not work because a is template dependent but the expression a; is not.
That is, the meaning you intend of a depends on a template argument (in your case K), but the expression that contains it does not. And since the compiler cannot be sure that the instantiation of A<K> will have a member named a, it simple assumes it does not.
There are several easy solutions, all of them involve using a template dependent expression so resolve a:
this->a = 3; //my favourite
A<K>::a = 3; //you found this one
EXAMPLE:
Let's see an example of why it should not work:
template <typename K> struct A
{
int a;
};
int a; //global variable to make things more interesting
template <typename K> struct B : A<K>
{
void foo()
{
a = 3; //does it refer to A<K>::a or ::a?
}
};
//template specialization of A<int>
template <> struct A<int>
{
};
B<float> bf; // what does bf.foo() do?
B<int> bi; //and bi.foo()?
Also, you can make the opposite situation:
template <typename K> struct A
{
};
int a; //global variable to make things more interesting
template <typename K> struct B : A<K>
{
void foo()
{
a = 3; //does it refer to A<K>::a or ::a?
}
};
//template specialization of A<int>
template <> struct A<int>
{
int a; //now the member variable is in the specialization
};
B<float> bf; // what does bf.foo() do?
B<int> bi; //and bi.foo()?
As you can see, the standard C++ behavior is there for you protection, so that the code you write in a template will always refer -more or less- to the same things:
a: will be always the non template (global in the example) variable, if available. If not, it is a compiler error.
this->a: will be always the member variable, if available. If not, a compiler error.
This question already has an answer here:
Closed 12 years ago.
Possible Duplicate:
problem with template inheritance
This code doesn't compile in GCC:
template <typename T>
struct Base
{
public:
int x;
};
template <typename B>
struct Middle : public B
{
};
template <typename T>
struct Sub : public Middle<Base<T> >
{
public:
void F()
{
x=1; // error: ‘x’ was not declared in this scope
}
};
If either Base or Sub weren't template classes, it wouldn't complain. VC handles it.
Why?
Use this->x = 1; to tell the compiler that x is a (template-) dependent name.
Note: What GCC does ot fine according to the standard, MSVC is just a bit more tolerant.
The following code doesn't compile with gcc, but does with Visual Studio:
template <typename T> class A {
public:
T foo;
};
template <typename T> class B: public A <T> {
public:
void bar() { cout << foo << endl; }
};
I get the error:
test.cpp: In member function ‘void B::bar()’:
test.cpp:11: error: ‘foo’ was not declared in this scope
But it should be! If I change bar to
void bar() { cout << this->foo << endl; }
then it does compile, but I don't think I have to do this. Is there something in the official specs of C++ that GCC is following here, or is it just a quirk?
David Joyner had the history, here is the reason.
The problem when compiling B<T> is that its base class A<T> is unknown from the compiler, being a template class, so no way for the compiler to know any members from the base class.
Earlier versions did some inference by actually parsing the base template class, but ISO C++ stated that this inference can lead to conflicts where there should not be.
The solution to reference a base class member in a template is to use this (like you did) or specifically name the base class:
template <typename T> class A {
public:
T foo;
};
template <typename T> class B: public A <T> {
public:
void bar() { cout << A<T>::foo << endl; }
};
More information in gcc manual.
Wow. C++ never ceases to surprise me with its weirdness.
In a template definition, unqualified names will no longer find members of a dependent base (as specified by [temp.dep]/3 in the C++ standard). For example,
template <typename T> struct B {
int m;
int n;
int f ();
int g ();
};
int n;
int g ();
template <typename T> struct C : B<T> {
void h ()
{
m = 0; // error
f (); // error
n = 0; // ::n is modified
g (); // ::g is called
}
};
You must make the names dependent, e.g. by prefixing them with this->. Here is the corrected definition of C::h,
template <typename T> void C<T>::h ()
{
this->m = 0;
this->f ();
this->n = 0
this->g ();
}
As an alternative solution (unfortunately not backwards compatible with GCC 3.3), you may use using declarations instead of this->:
template <typename T> struct C : B<T> {
using B<T>::m;
using B<T>::f;
using B<T>::n;
using B<T>::g;
void h ()
{
m = 0;
f ();
n = 0;
g ();
}
};
That's just all kinds of crazy. Thanks, David.
Here's the "temp.dep/3" section of the standard [ISO/IEC 14882:2003] that they are referring to:
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. [Example:
typedef double A;
template<class T> class B {
typedef int A;
};
template<class T> struct X : B<T> {
A a; // a has typedouble
};
The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T>. ] [Example:
struct A {
struct B { /* ... */ };
int a;
int Y;
};
int a;
template<class T> struct Y : T {
struct B { /* ... */ };
B b; //The B defined in Y
void f(int i) { a = i; } // ::a
Y* p; // Y<T>
};
Y<A> ya;
The members A::B, A::a, and A::Y of the template argument A do not affect the binding of names in Y<A>. ]
This changed in gcc-3.4. The C++ parser got much more strict in that release -- per the spec but still kinda annoying for people with legacy or multi-platform code bases.
The main reason C++ cannot assume anything here is that the base template can be specialized for a type later. Continuing the original example:
template<>
class A<int> {};
B<int> x;
x.bar();//this will fail because there is no member foo in A<int>
VC doesn't implemented two-phase lookup, while GCC does. So GCC parses templates before they are instantiated and thus finds more errors than VC.
In your example, foo is a dependent name, since it depends on 'T'. Unless you tell the compiler where it comes from, it cannot check the validity of the template at all, before you instantiate it.
That's why you have to tell the compiler where it comes from.