Accessing a protected member in a derived class

T

Tom

Hi NG,

The following example does not compile using g++. The compiler says
"error: ‘iMyVal’ was not declared in this scope" in Method MyFunction
of the derived class:

template<typename T>
class MyBase
{
protected:
int iMyVal;
};

template<typename T>
class MyDerived : public MyBase<T>
{
void MyFunction()
{
// this->iMyVal = 10;
iMyVal = 10;
}
};

int main()
{
MyDerived<int> derived;
return 0;
}

Using the line with the this-> works fine. After removing the template
stuff the code compiles also fine without this->:

class MyBase
{
protected:
int iMyVal;
};

class MyDerived : public MyBase
{
void MyFunction()
{
iMyVal = 10;
}
};

int main()
{
MyDerived derived;
return 0;
}

My question is: Why do I need the "this->" in the first example. Why
do I not need the this anymore when I remove the template parameter of
the class?

Tom
 
J

Johannes Schaub

Tom said:
Hi NG,

The following example does not compile using g++. The compiler says
"error: ‘iMyVal’ was not declared in this scope" in Method MyFunction
of the derived class:

template<typename T>
class MyBase
{
protected:
int iMyVal;
};

template<typename T>
class MyDerived : public MyBase<T>
{
void MyFunction()
{
// this->iMyVal = 10;
iMyVal = 10;
}
};

int main()
{
MyDerived<int> derived;
return 0;
}

Using the line with the this-> works fine. After removing the template
stuff the code compiles also fine without this->:

"iMyVal" is an unqualified names. Unqualified name lookup won't look into
base classes whose type depends on template parameters (such as "T", or
"MyBase<T>").

If you say "this->iMyVal" or "MyDerived::iMyVal", you are using class member
access and qualified name lookup respectively. Those don't ignore dependent
base classes, and will thus find "int iMyVal;" when instantiating
"MyDerived<int>::MyFunction".
 
V

Victor Bazarov

"iMyVal" is an unqualified names. Unqualified name lookup won't look into
base classes whose type depends on template parameters (such as "T", or
"MyBase<T>").

If you say "this->iMyVal" or "MyDerived::iMyVal", you are using class member
access and qualified name lookup respectively. Those don't ignore dependent
base classes, and will thus find "int iMyVal;" when instantiating
"MyDerived<int>::MyFunction".

To the OP:
http://www.parashift.com/c++-faq-lite/templates.html#faq-35.19

To Johannes:
http://www.parashift.com/c++-faq-lite/how-to-post.html#faq-5.5

V
 
J

Johannes Schaub

Victor said:

The FAQ has it subtly wrong. Which is why I didn't gave him the FAQ entry.
Where the Standard says that the base declaration is not found because the
name is unqualified, the FAQ says that the declaration is not found because
the name is non-dependent. This is a common misconception (I notified the
FAQ author about this, and he noted that problem and thanked to me. But last
time I checked, he hadn't fixed this problem yet).
 
T

Tom

"iMyVal" is an unqualified names. Unqualified name lookup won't look into
base classes whose type depends on template parameters (such as "T", or
"MyBase<T>").

If you say "this->iMyVal" or "MyDerived::iMyVal", you are using class member
access and qualified name lookup respectively. Those don't ignore dependent
base classes, and will thus find "int iMyVal;" when instantiating
"MyDerived<int>::MyFunction".

Thanks for the explanations. So this is something which makes life a
bit easier for the compiler, right? Or is there another reason to
restrict the name lookup. I guess not because I think MS VS accepts
the code without this.
 
A

Alf P. Steinbach

[snip]

The FAQ has it subtly wrong. Which is why I didn't gave him the FAQ entry.
Where the Standard says that the base declaration is not found because the
name is unqualified, the FAQ says that the declaration is not found because
the name is non-dependent. This is a common misconception

Let's do a reality check.

There is an error when "imMyVal" is a non-dependent name, and a concrete
example has been given.

Would there have been an error if instead of "imMyVal" there had been a
dependent name (of course, it may then have to be of different syntactic
form)?

If the answer to that question is "no", then we have established that at
least within the framework of this concrete example the dependency of
the name corresponds directly to the error, i.e. that in this example
dependent/non-dependent determines the error, as the FAQ says.

If the answer, on the other hand, is "yes", then there should exist a
concrete example of that?


(I notified the
FAQ author about this, and he noted that problem and thanked to me. But last
time I checked, he hadn't fixed this problem yet).

I think that if you gave Marshall a concrete code example that was
incompatible with the explanation in the FAQ, then he would update the
FAQ promptly, unless he was prevented by e.g. health issues.


Cheers & hth.,

- Alf
 
K

Kouisawang

Hi NG,

The following example does not compile using g++. The compiler says
"error: ‘iMyVal’ was not declared in this scope" in Method MyFunction
of the derived class:

template<typename T>
class MyBase
{
protected:
  int iMyVal;

};

template<typename T>
class MyDerived  : public MyBase<T>
{
  void MyFunction()
  {
    //    this->iMyVal = 10;
    iMyVal = 10;
  }

};

int main()
{
  MyDerived<int> derived;
  return 0;

}

Using the line with the this-> works fine. After removing the template
stuff the code compiles also fine  without this->:

class MyBase
{
protected:
  int iMyVal;

};

class MyDerived  : public MyBase
{
  void MyFunction()
  {
    iMyVal = 10;
  }

};

int main()
{
  MyDerived derived;
  return 0;

}

My question is: Why do I need the "this->" in the first example. Why
do I not need the this anymore when I remove the template parameter of
the class?

Tom

Template is a compile time process. The pointer can access it without
specifying the <T> because it goes directly to the memory where the
parameter stored. If you don't wanna use pointer, try
MyBase<T>::iMyVal. This will also happen to public members also.
 
J

Johannes Schaub

wrote:
Victor said:
On 7/23/2011 7:39 AM, Johannes Schaub wrote:
Tom wrote:

Hi NG,

The following example does not compile using g++. The compiler says
"error: ‘iMyVal’ was not declared in this scope" in Method MyFunction
of the derived class:

template<typename T>
class MyBase
{
protected:
int iMyVal;
};

template<typename T>
class MyDerived : public MyBase<T>
{
void MyFunction()
{
// this->iMyVal = 10;
iMyVal = 10;
}
};

int main()
{
MyDerived<int> derived;
return 0;
}
[snip]

The FAQ has it subtly wrong. Which is why I didn't gave him the FAQ
entry. Where the Standard says that the base declaration is not found
because the name is unqualified, the FAQ says that the declaration is not
found because the name is non-dependent. This is a common misconception

Let's do a reality check.

There is an error when "imMyVal" is a non-dependent name, and a concrete
example has been given.

Would there have been an error if instead of "imMyVal" there had been a
dependent name (of course, it may then have to be of different syntactic
form)?

Yes there would have been. The C++ Standard specifically says: Unqualified
lookup ignores the dependent base classes both at the time of definition of
the template and at instantiation of the template.

If the answer to that question is "no", then we have established that at
least within the framework of this concrete example the dependency of
the name corresponds directly to the error, i.e. that in this example
dependent/non-dependent determines the error, as the FAQ says.

If the answer, on the other hand, is "yes", then there should exist a
concrete example of that?

I can't show an example using a function call, because if you make the name
dependent, the rules for lookup of dependent function names dictate that
unqualified lookup only consideres declarations of the definition context.
Declarations visible only when instantiating are only considered by argument
dependent lookup.

But still, the following code contains a dependent name "f", and will ignore
the dependent base class, even if the lookup for dependent function names
would otherwise have looked-up in the scope of the class at the time of
instantiation again.

Example:

template<typename T>
struct A {
void f(T) { }
};

template<typename T>
struct B : A<T> {
void g() {
f(T());
}
};

int main() { B<int> b; b.g(); }

I can argue for my position by noting that the Standard doesn't define what
happens to the dependent base class in the lookup within the definition
context, if we would strike out the rule that unqualified lookup ignores
dependent base class. The obvious interpretation would be to say "We have to
look in the definition context into a base class that is dependent. Hence we
have to delay lookup, waiting for it to become complete". The Standard
doesn't need to handle this case because it explicitly says that unqualified
lookup ignores dependent base classes, like I said above.

In fact the "obvious interpretation" was spelled out in the C++98 Standard,
which worded the rule differently (but to the same effect): It said that
dependent base classes are not examined during lookup until the class is
instantiated. The absence of a definition of the case of what happens to
dependent base classes for lookup at the definition context was answered by
that. Later DRs that were incorporated into C++03 noted that this is
unnecessarily convoluted and easily leads to misinterpretations and changed
it to simply say that unqualified lookup ignores dependent base classes.

I can show non-call examples that prove my point:

template<typename U>
struct A {
typedef int T;
};

template<typename T>
struct B : A<T> {
void f() {
T t;
}
};

The dependent name "T" is not found in the dependent base class, even though
it would if ignoring dependent base classes would solely be done because of
names being non-dependent. You may argue that lookup of names like "T" above
is not redone at instantiation, but I think that the spec requires it to be,
because "T" is type-dependent. Whether or not that is intuitive is another
matter, though. C++98 explicitly ruled that the "T" declared in the
dependent base class at instantiation time cannot hide the template
parameter when lookup is done at instantiation again.
 
A

Alf P. Steinbach

wrote:
Victor Bazarov wrote:
On 7/23/2011 7:39 AM, Johannes Schaub wrote:
Tom wrote:

Hi NG,

The following example does not compile using g++. The compiler says
"error: ‘iMyVal’ was not declared in this scope" in Method MyFunction
of the derived class:

template<typename T>
class MyBase
{
protected:
int iMyVal;
};

template<typename T>
class MyDerived : public MyBase<T>
{
void MyFunction()
{
// this->iMyVal = 10;
iMyVal = 10;
}
};

int main()
{
MyDerived<int> derived;
return 0;
}
[snip]

The FAQ has it subtly wrong. Which is why I didn't gave him the FAQ
entry. Where the Standard says that the base declaration is not found
because the name is unqualified, the FAQ says that the declaration is not
found because the name is non-dependent. This is a common misconception

Let's do a reality check.

There is an error when "imMyVal" is a non-dependent name, and a concrete
example has been given.

Would there have been an error if instead of "imMyVal" there had been a
dependent name (of course, it may then have to be of different syntactic
form)?

Yes there would have been. The C++ Standard specifically says: Unqualified
lookup ignores the dependent base classes both at the time of definition of
the template and at instantiation of the template.

If the answer to that question is "no", then we have established that at
least within the framework of this concrete example the dependency of
the name corresponds directly to the error, i.e. that in this example
dependent/non-dependent determines the error, as the FAQ says.

If the answer, on the other hand, is "yes", then there should exist a
concrete example of that?

I can't show an example using a function call, because if you make the name
dependent, the rules for lookup of dependent function names dictate that
unqualified lookup only consideres declarations of the definition context.

But isn't your example below an example using a function call?

Declarations visible only when instantiating are only considered by argument
dependent lookup.

But still, the following code contains a dependent name "f", and will ignore
the dependent base class, even if the lookup for dependent function names
would otherwise have looked-up in the scope of the class at the time of
instantiation again.

Example:

template<typename T>
struct A {
void f(T) { }
};

template<typename T>
struct B : A<T> {
void g() {
f(T());
}
};

int main() { B<int> b; b.g(); }

I think, send this example to Marshall?

I can argue for my position by noting that the Standard doesn't define what
happens to the dependent base class in the lookup within the definition
context, if we would strike out the rule that unqualified lookup ignores
dependent base class. The obvious interpretation would be to say "We have to
look in the definition context into a base class that is dependent. Hence we
have to delay lookup, waiting for it to become complete". The Standard
doesn't need to handle this case because it explicitly says that unqualified
lookup ignores dependent base classes, like I said above.

In fact the "obvious interpretation" was spelled out in the C++98 Standard,
which worded the rule differently (but to the same effect): It said that
dependent base classes are not examined during lookup until the class is
instantiated. The absence of a definition of the case of what happens to
dependent base classes for lookup at the definition context was answered by
that. Later DRs that were incorporated into C++03 noted that this is
unnecessarily convoluted and easily leads to misinterpretations and changed
it to simply say that unqualified lookup ignores dependent base classes.

I can show non-call examples that prove my point:

template<typename U>
struct A {
typedef int T;
};

template<typename T>
struct B : A<T> {
void f() {
T t;
}
};

The dependent name "T" is not found in the dependent base class, even though
it would if ignoring dependent base classes would solely be done because of
names being non-dependent. You may argue that lookup of names like "T" above
is not redone at instantiation, but I think that the spec requires it to be,
because "T" is type-dependent. Whether or not that is intuitive is another
matter, though. C++98 explicitly ruled that the "T" declared in the
dependent base class at instantiation time cannot hide the template
parameter when lookup is done at instantiation again.

Sounds like you were right.

Also sounds like can'o'worms. :)

Like, as if too much has been forcibly pressed into too few concepts, in
the standard.


Cheers,

- Alf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top