template<typename T, typename>
class A
{
public:
void AFunc() const {}
protected:
A() {} // Must be subclassed to be instantiated
T t_;
};
template <typename T>
struct B
{
struct C { int j; };
struct D : public A<int,C>
{
int DFunc()
{
AFunc(); // Error: AFunc undefined!
return t_; // Error: t_ undefined!
}
};
D d_;
};
// Instantiate B
B<int> b;
I can get things to compile by:
1. Putting the compiler in "relaxed" mode in g++ or Comeau,
2. Making C a non-nested class by putting it at namespace/file scope,
3. Changing the template parameters to A (e.g., change a line above to
"struct D : public A<int,int>"), or
4. Making B a non-template class.
Obviously, none of these solutions is ideal. Any suggestions or ideas
why this happens?
M
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
-Ganesh.
Two-phase name lookup. To fix it, write Dfunc like this:
int Dfunc()
{
A<int,C>::AFunc();
return A<int,C>::t_;
}
But, you ask, where is the dependent name which causes the
delayed lookup? It's in 14.6.1/2d: "Within the scope of a
class template, when the unqualified name of a nested class
of the class template is referred to, it is equivalent to
the name of the nested class qualified by the name of the
enclosing class template." So what you actually have is
template <typename T>
struct B
{
struct C { int j; };
struct D : public A<int, typename B<T>::C>
{
int DFunc()
{
AFunc(); // Error: AFunc undefined!
return t_; // Error: t_ undefined!
}
};
D d_;
};
That makes D's base class a dependent name, and that
triggers the problem, even though it's not possible
for C to be anything but the struct C you've written.
Inside a template definition there are dependent and non-dependent
names (which do or don't depend on the template parameters). AFunc
and t_ in your statements are non-dependent and as such are looked
up when parsing the template definition. You want these names to be
found in the base class A<int,C>, but C is B<T>::C, i.e. a dependent
name and thus A<int,C> is a dependent base of D. The problem is that
dependent bases are not examined for non-dependent names. To solve
the problem make these names dependent. The easiest possibility is
to use "this->":
this->AFunc();
return this->t_;
The other possibility is explicit qualification:
A<int,C>::AFunc();
return A<int,C>::t_;
> }
> };
>
> D d_;
> };
[...]
Vladimir Marko
Thanks to you and all who answered. That helps. (I forgot to mention
that explicitly qualifying the inherited members also allows the code
to compile. While that's not an unacceptable solution from a design
perspective, it was aesthetically displeasing in the real context.)
A follow-up question: This two-stage name lookup is likely there for a
good reason. Just for edification, can someone give me an example or
two where the rule helps rather than hinders?
Cheers!
M