Not able to access inherited protected member?

S

Shriramana Sharma

Hello. Please consider the following minimized example for the problem I amfacing.
template < typename T >
struct item {
typedef item<T> * pointer ;
pointer prev, next ;
} ;

template < typename ItemPtr >
struct iterator_base {
public:
iterator_base () : ptr(0) {}
protected:
ItemPtr ptr ;
explicit iterator_base ( ItemPtr p ) : ptr(p) {}
} ;

template < typename T >
struct iterator : public iterator_base < item<T> * > {
template<typename> friend class const_iterator ;
} ;

template < typename T >
struct const_iterator : public iterator_base < const item<T> * > {
const_iterator () {}
const_iterator ( const iterator<T> & it ) : ptr(it.ptr) {}
} ;

int main () {
iterator<int> it ;
const_iterator<int> cit ;
cit = it ;
}
<<<------------------------

I get the error messages:
$ g++ -o iterator-base-minimal-2 iterator-base-minimal-2.cpp
iterator-base-minimal-2.cpp: In constructor ‘const_iterator<T>::const_iterator(const iterator<T>&)’:
iterator-base-minimal-2.cpp:28:46: error: class ‘const_iterator<T>’ does not have any field named ‘ptr’
const_iterator ( const iterator<T> & it ) : ptr(it.ptr) {}
^
$ clang++ -o iterator-base-minimal-2 iterator-base-minimal-2.cpp
iterator-base-minimal-2.cpp:28:46: error: member initializer 'ptr' does notname a non-static data member or base class
const_iterator ( const iterator<T> & it ) : ptr(it.ptr) {}
^~~~~~~~~~~
1 error generated.
<<<-------------------------------

I am not sure why I can't access ptr which is a protected member of a parent class. OK so anyhow I thought I'll explicitly scope it by the base type and so I changed the const_iterator definition to:
template < typename T >
struct const_iterator : public iterator_base < const item<T> * > {
typedef iterator_base < const item<T> * > base ;
const_iterator () {}
const_iterator ( const iterator<T> & it ) : base::ptr(it.ptr) {}
} ;
<<<-------------------------------

But now I get even more cryptic error messages:
$ g++ -o iterator-base-minimal-2 iterator-base-minimal-2.cpp
iterator-base-minimal-2.cpp: In instantiation of ‘const_iterator<T>::const_iterator(const iterator<T>&) [with T = int]’:
iterator-base-minimal-2.cpp:35:6: required from here
iterator-base-minimal-2.cpp:28:62: error: no type named ‘ptr’ in ‘struct iterator_base<const item<int>*>’
const_iterator ( const iterator<T> & it ) : base::ptr(it.ptr) {}
^
$ clang++ -o iterator-base-minimal-2 iterator-base-minimal-2.cpp
iterator-base-minimal-2.cpp:28:52: error: typename specifier refers to non-type member 'ptr' in 'iterator_base<const item<int> *>'
const_iterator ( const iterator<T> & it ) : base::ptr(it.ptr) {}
^~~
iterator-base-minimal-2.cpp:35:8: note: in instantiation of member function'const_iterator<int>::const_iterator' requested here
cit = it ;
^
iterator-base-minimal-2.cpp:13:10: note: referenced member 'ptr' is declared here
ItemPtr ptr ;
^
iterator-base-minimal-2.cpp:28:52: error: 'ptr' is a protected member of 'iterator_base<const item<int> *>'
const_iterator ( const iterator<T> & it ) : base::ptr(it.ptr) {}
^
iterator-base-minimal-2.cpp:13:10: note: must name member using the type ofthe current context 'const_iterator<int>'
ItemPtr ptr ;
^
2 errors generated.
<<<-------------------------------

I'm using GCC 4.8.1 and Clang 3.2 on Kubuntu Raring 64-bit. Can anyone please tell me what the problem is and how I can solve it? Thank you very much!
 
V

Victor Bazarov

Hello. Please consider the following minimized example for the problem I am facing.
[...]

Isn't it essentially the same as

struct B {
int a;
};

struct D : B {
D(int a1) : a(a1) {}
};

?

You're trying to initialize (in the class initialiser list) a member
that is not of that class. It's not allowed.

V
 
S

Shriramana Sharma

You're trying to initialize (in the class initialiser list) a member
that is not of that class. It's not allowed.

Thank you very much for your reply. So moving the base::ptr initialization to within the braces of the constructor fixes the situation for the above example.

However this other minimal example seems to be able to take an initializer for the base class within the initializer list:

template < typename T >
struct iterator_base
{
protected:
T * ptr ;
} ;

template < typename T >
struct iterator : public iterator_base<T> {} ;

template < typename T >
struct const_iterator : public iterator_base<T>
{
typedef iterator_base<T> base ;
const_iterator () : base::ptr(0) {}
const_iterator ( const iterator<T> & it ) : base::ptr(it.ptr) {}
} ;

int main () {}

Can you clarify what is the difference between this situation and the above? Thanks again!
 
Ö

Öö Tiib

Can you clarify what is the difference between this situation and the above? Thanks again!

Can you clarify why you push those ill pieces of code?

Situation is different since you do not instantiate the template
(main is empty) and so the compiler is not required to diagnose it.
Both examples are invalid C++. Try to write valid C++.
 
S

Shriramana Sharma

Can you clarify why you push those ill pieces of code?

I'm sorry but I don't understand what you mean by "pushing ill pieces of code". What am I "pushing" here?

I am not a professional programmer but am trying to use C++ for some academic projects of mine. Is this the wrong forum to ask doubts about "why doesn't this work" in order to learn how to write C++ code correctly? It is obvious I do not have the experience you people do, which is why I am asking these simple (to you) questions here.
Situation is different since you do not instantiate the template
(main is empty) and so the compiler is not required to diagnose it.

OK thanks -- this reply would have sufficed. I found that by instantiating the template the error is indeed flagged.
Both examples are invalid C++. Try to write valid C++.

I *am* trying to write valid C++. For that I have to know what is valid C++(the standard is not all that simple to understand directly for everyone -- I hope you will not deny that). The very fact that the compiler rejects it already tells me that what I wrote is invalid C++. I am trying to understand *why* it is invalid C++.

If you have the time and patience, help me and I am thankful for your help.If you don't have the time, just ignore my question. Please have patience with someone who is not as experienced as you.

Thank you for your pointer though.
 
Ö

Öö Tiib

I'm sorry but I don't understand what you mean by "pushing ill pieces
of code". What am I "pushing" here?

I am sorry, too, but Victor said that it is not allowed. Then you try to
trick it otherwise, then third way. That I called "pushing". You are
trying something in a strange way but what it is? What is your ultimate
goal? Can you clarify why?
I am not a professional programmer but am trying to use C++ for some
academic projects of mine. Is this the wrong forum to ask doubts about
"why doesn't this work" in order to learn how to write C++ code correctly?

It is correct forum but such path of learning C++ will not be easy. In fact
learning C++ by trial and error is as hard as learning to fly airplane
by trial and error.
OK thanks -- this reply would have sufficed. I found that by
instantiating the template the error is indeed flagged.

Beware that C++ compiler may compile very lot of broken code without
any diagnostics. C++ standard tells things like "code is ill-formed,
no diagnostics required" or "behavior is undefined" about such code.
Compilers (most notably CLang) still aim to diagnose as many
errors as possible but it is hard.
I *am* trying to write valid C++. For that I have to know what is valid
C++ (the standard is not all that simple to understand directly for
everyone -- I hope you will not deny that).

Yes, also learning to fly airplane by studying its construction details
is hard. There are only the formal technical details in standard. Audience
is meant to be the C++ tool and compiler writers.

Learn C++ by reading books that teach to use it. Most of us have several
such books. Fine list of books:
http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list
The very fact that the compiler rejects it already tells me that what I
wrote is invalid C++. I am trying to understand *why* it is invalid C++.

It is rejecting because what you try is to break encapsulation. Constructor
is meant for initializing virtual bases, bases and members. The bases and
members however have full right to their own privacy and so are meant to
initialize their details themselves:

template < typename T >
struct iterator_base
{
protected:
iterator_base( T* p )
: ptr(p)
{}

T* ptr;
};

template < typename T >
struct const_iterator
: public iterator_base<T>
{
const_iterator()
: iterator_base( 0 )
{}

const_iterator( const_iterator<T> const& it )
: iterator_base( it.ptr )
{}
};

The protected members are exceptional. Most books suggest to have the
data members private unless there are some rather good reasons.
If you have the time and patience, help me and I am thankful for your help.

My question was not meant to insult you or to refuse to help you. It was
to alert you to step back and analyze what you do. The way chosen seems
to be non-optimal.
 
S

Shriramana Sharma

No, this is not fix, rather an abuse. In general, all data should be
private, IOW all initialization must be done by the class' own
constructors, not by constructors of some other class (derived or not).

Make your data private, provide a constructor for the base class and use
this in the derived class constructor initialization list.

Righto -- thank you for this pointer. Because Victor had initially insertedthe phrase "in the class initializer list", I thought it is OK to do the initialization within the braces. Now I understand that while that will be grammatically correct, it is inadvisable and unless it's a plain struct not intended for inheritance (the way D enforces) one should encapsulate data.
 
S

Shriramana Sharma

I am sorry, too, but Victor said that it is not allowed. Then you try to
trick it otherwise, then third way. That I called "pushing".

As I mention in my reply to Paavo above, the fact that Victor had included the phrase "in the member initializer list" (though it was within parans) lead me to only learn the fact that initialization of indirect sub-objects is not permissible in the member initializer list. Moving the same initialization to the braces compiled, so I thought that was OK.
You are
trying something in a strange way but what it is? What is your ultimate
goal? Can you clarify why?

Heh there is no "ultimate goal" except to learn enough of the language for the project I am working on. :)
learning C++ by trial and error is as hard as learning to fly airplane
by trial and error.

Well said. And I also take your point about not trying to learn C++ using the text of the standard.
It is rejecting because what you try is to break encapsulation. Constructor
is meant for initializing virtual bases, bases and members. The bases and
members however have full right to their own privacy and so are meant to
initialize their details themselves:

Now I understand this.
My question was not meant to insult you or to refuse to help you. It was
to alert you to step back and analyze what you do. The way chosen seems
to be non-optimal.

Thank you for the advice. It is truly appreciated. :)

Shriramana.
 

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,913
Messages
2,570,027
Members
46,421
Latest member
WaylonBran

Latest Threads

Top