Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

(Inherited) Member access requires "this"

1 view
Skip to first unread message

Jason

unread,
Nov 23, 2009, 4:45:54 PM11/23/09
to
Hello,

I didn't understand why I need "this" in following example. I don't
have standard book. It works on Microsoft compiler but not on GCC.

template<class T>
class outer
{
public:
class forward1;
class forward2;

class forward1
{
public:
void foo() { }
const char* p;
};
class forward2 : public forward1
{
public:
forward2() { m_p = NULL; } // error: m_p was not declared in
this scope
void bar() { foo(); } // error: foo() was not declared in this
scope
};
};

If I access those members via "this->" then it seems to compile with
gcc. I guess it also works when I prepend forward1:: as well.

Is there a particular reason for this? Is it because m_p may have been
declared in outer<T> as well?

Cheers!

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Paul Bibbings

unread,
Nov 24, 2009, 12:50:38 AM11/24/09
to
Jason wrote:
> Hello,
>
> I didn't understand why I need "this" in following example. I don't
> have standard book. It works on Microsoft compiler but not on GCC.
>
> template<class T>
> class outer
> {
> public:
> class forward1;
> class forward2;
>
> class forward1
> {
> public:
> void foo() { }
> const char* p;
> };
> class forward2 : public forward1
> {
> public:
> forward2() { m_p = NULL; } // error: m_p was not declared in
> this scope
> void bar() { foo(); } // error: foo() was not declared in this
> scope
> };
> };
>
> If I access those members via "this->" then it seems to compile with
> gcc. I guess it also works when I prepend forward1:: as well.
>
> Is there a particular reason for this? Is it because m_p may have been
> declared in outer<T> as well?
>
> Cheers!
>

The clue here is that everything compiles fine in gcc also if you remove the
template<class T> on class outer. It has everything to do with dependant names
and thus the rules of "dependent name lookup." Note that gcc is compiling this
correctly here, and you will see the same failure of your code if you pass it
into Comeau Online (www.comeaucomputing.com/tryitout) in strict C++03 mode.
(Disabling strict in comeau will allow it to compile as your Microsoft compiler
does.)

For the explanation you might want to look at the entry "Why is this->member
needed?" on comeau's "Tech Talk" faq at
http://www.comeaucomputing.com/techtalk/templates/#whymembernotfound . As far as
I can see from a very quick reading it looks like this article addresses
exactly the issue as you have encountered it here.

Hope this helps.

Regards

Paul Bibbings

red floyd

unread,
Nov 24, 2009, 12:52:28 AM11/24/09
to
On Nov 23, 1:45 pm, Jason <bumperman...@hotmail.com> wrote:
> Hello,
>
> I didn't understand why I need "this" in following example. I don't
> have standard book. It works on Microsoft compiler but not on GCC.
>
> template<class T>
> class outer
> {
> public:
> class forward1;
> class forward2;
>
> class forward1
> {
> public:
> void foo() { }
> const char* p;
> };
> class forward2 : public forward1
> {
> public:
> forward2() { m_p = NULL; } // error: m_p was not declared in
> this scope
> void bar() { foo(); } // error: foo() was not declared in this
> scope
> };
>
> };
>
> If I access those members via "this->" then it seems to compile with
> gcc. I guess it also works when I prepend forward1:: as well.
>
> Is there a particular reason for this? Is it because m_p may have been
> declared in outer<T> as well?
>

See FAQ 35.19 http://parashift.com/c++-faq-lite/templates.html#faq-35.19

Basically, because forward1 is a member of a template, it becomes a
dependent base
class. You need to use one of the two constructs you've already
found.

stork

unread,
Nov 24, 2009, 12:53:32 AM11/24/09
to

> I didn't understand why I need "this" in following example. I don't
> have standard book. It works on Microsoft compiler but not on GCC.

Microsoft's behavior is actually wrong and GCC's is right. I can't
remember the exact details but there is actually an edge case where
the compiler actually cannot know the right thing to do and so
Microsoft makes an assumption that is unwarranted in the general
case. As a rule, GCC is going to go more by the standard exactly,
unless you explicitly enable their extensions, but Microsoft will
deviate from the standard when it feels like it.

Andrey Tarasevich

unread,
Nov 24, 2009, 12:57:20 AM11/24/09
to
Jason wrote:
> ...

> template<class T>
> class outer
> {
> public:
> class forward1;
> class forward2;
>
> class forward1
> {
> public:
> void foo() { }
> const char* p;
> };
> class forward2 : public forward1
> {
> public:
> forward2() { m_p = NULL; } // error: m_p was not declared in
> this scope
> void bar() { foo(); } // error: foo() was not declared in this
> scope
> };
> };
>
> If I access those members via "this->" then it seems to compile with
> gcc. I guess it also works when I prepend forward1:: as well.

This will not compile with _any_ compiler. You don't have any 'm_p'
declared anywhere. Your 'forward1' has a member named 'p', not 'm_p'.
Apparently, you were compiling something different with GCC. In any
case, try posting more or less _real_ code.

> Is there a particular reason for this? Is it because m_p may have been
> declared in outer<T> as well?

The reason for this is that the inner class 'forward1' is actually just
a short form of 'outer<T>::forward1', i.e. technically it is a dependent
type. It depends on the template parameter 'T'. You use that
'outer<T>::forward1' as a base class for your 'forward2'. Dependent base
classes are not checked by unqualified name lookup, which is why the
compiler cannot find neither 'foo' nor 'p' from method definitions of
'forward2'.

In other words, the problem you are experiencing is the same as in the
following simple example

template <typename T> struct B {
int i;
};

template <typename T> struct D : B<T> {
D() {
i = 5; /* ERROR: can't find 'i' */
}
};

except that in your case it is much less obvious due to the fact that
you are using nested classes in a class template.

To solve it you should either use qualified name syntax

template <typename T> struct D : B<T> {
D() {
B<T>::i = 5; /* OK */
}
};

or explicit class member access syntax

template <typename T> struct D : B<T> {
D() {
this->i = 5; /* OK */
}
};

which is what you already did to make it work.

--
Best regards,
Andrey Tarasevich

Martin B.

unread,
Nov 24, 2009, 11:00:36 AM11/24/09
to
stork wrote:
>> I didn't understand why I need "this" in following example. I don't
>> have standard book. It works on Microsoft compiler but not on GCC.
>
> (...) As a rule, GCC is going to go more by the standard exactly,

> unless you explicitly enable their extensions, but Microsoft will
> deviate from the standard when it feels like it.
>

Of course one should know about /Za (Disable Language Extensions) for
Visual C++. This often helps to resolve confusion about differences btw.
VC and other compilers. (Not sure if it "helps" in this case.)
Also: I had the impression that MS - since VS2005/VC8 - is really trying
to get to grips with std compliance.
I'd rather say that MS is trying to do the pragmatic thing for the
platforms and legacy it supports rather than religiously following a std
that's moving at a glacial pace at best. (And yes, sometimes it's a mess
and sometimes it works.) :-)

cheers,
Martin

CornedBee

unread,
Nov 24, 2009, 11:20:34 AM11/24/09
to
On Nov 24, 6:53 am, stork <tbandrow...@treatyist.com> wrote:
> > I didn't understand why I need "this" in following example. I don't
> > have standard book. It works on Microsoft compiler but not on GCC.
>
> Microsoft's behavior is actually wrong and GCC's is right. I can't
> remember the exact details but there is actually an edge case where
> the compiler actually cannot know the right thing to do and so
> Microsoft makes an assumption that is unwarranted in the general
> case.

MSC makes no assumptions. Its template mechanism works differently
from what would be expected, and this has the consequence that MSC
compiles code that is actually ill-formed, and that its name lookup
isn't quite right - in some edge cases, MSC will bind to a different
name than conforming compilers, but those cases are very contrived.
The thing is that GCC and probably most other compilers store
templates as ASTs, which get cloned with substitutions for
instantiation. To build an AST, some symbols must be looked up at
template parse time; thus various requirements such as the name lookup
thing, as well as the typename and template keywords in dependent
names.
MSC, on the other hand, stores templates pretty much as token
sequences, with only minimal validation. It doesn't look up names at
all during parsing, only during instantiation. As a consequence, it
never has to deal with dependent contexts. I believe there are even
some cases where you can sneak in invalid syntax that isn't detected
until instantiation time.

0 new messages