portected access: template derivation vs. class derivation

S

Steven T. Hatton

In the following code, the class B will not compile if the function da is
uncommented. I took exactly the same code, and turned it into templates.
Never used the template parameter in anything but the baseclass name in the
derived class, and the code compiles without any complaints about protected
access. Why? What is different about the templated code?

class A{
public:
A(const int& x_=0): x(x_){}
protected:
int x;
};

class B: public A{
public:
B(const int& x_=0): A(x_){}
/*
B& da(const int& da_)
{
this->a.x += da_; // error: protected access
return *this;
}
*/
protected:
A a;
};

/*Same as above but with templates*/
template<typename T>
class AT{
public:
AT(const int& x_=0): x(x_){}
protected:
int x;
};

template<typename T>
class BT: public AT<T>{
public:
BT(const int& x_=0): AT<T>(x_){}

BT& da(const int& da_)
{
this->a.x += da_; // works fine
return *this;
}

protected:
AT<T> a;
};
 
V

Victor Bazarov

Steven said:
In the following code, the class B will not compile if the function da is
uncommented. I took exactly the same code, and turned it into templates.
Never used the template parameter in anything but the baseclass name in the
derived class, and the code compiles without any complaints about protected
access. Why? What is different about the templated code?

The templated code is not really "compiled" unless you attempt to
instantiate the template. Did you? Where is the rest of the code?
Was there anything else? If there wasn't, then all the compiler did
for your templates was checking the syntax. Names were not resolved,
access rights weren't checked, etc.
class A{
public:
A(const int& x_=0): x(x_){}
protected:
int x;
};

class B: public A{
public:
B(const int& x_=0): A(x_){}
/*
B& da(const int& da_)
{
this->a.x += da_; // error: protected access
return *this;
}
*/
protected:
A a;
};

/*Same as above but with templates*/
template<typename T>
class AT{
public:
AT(const int& x_=0): x(x_){}
protected:
int x;
};

template<typename T>
class BT: public AT<T>{
public:
BT(const int& x_=0): AT<T>(x_){}

BT& da(const int& da_)
{
this->a.x += da_; // works fine
return *this;
}

protected:
AT<T> a;
};

V
 
K

Kai-Uwe Bux

Steven said:
In the following code, the class B will not compile if the function da is
uncommented. I took exactly the same code, and turned it into templates.
Never used the template parameter in anything but the baseclass name in
the derived class, and the code compiles without any complaints about
protected
access. Why? What is different about the templated code?

class A{
public:
A(const int& x_=0): x(x_){}
protected:
int x;
};

class B: public A{
public:
B(const int& x_=0): A(x_){}
/*
B& da(const int& da_)
{
this->a.x += da_; // error: protected access
return *this;
}
*/
protected:
A a;
};

/*Same as above but with templates*/
template<typename T>
class AT{
public:
AT(const int& x_=0): x(x_){}
protected:
int x;
};

template<typename T>
class BT: public AT<T>{
public:
BT(const int& x_=0): AT<T>(x_){}

BT& da(const int& da_)
{
this->a.x += da_; // works fine
return *this;
}

protected:
AT<T> a;
};

Hi,


did you actually instantiate the template in main(), or did you just
compile this file as a compilation unit? In the later case, it would be
unwise for the compiler to complain: If this template is used as part of a
bigger programm, some other part might provide a specialization for AT<>
where the member x is not protected. For those types, BT<> should not cause
any troubles at all. Beware that the interface of AT<T> may depend on T.

Rest assured, you will see tons of inscrutable error messages when you
actually instantiate the template so that access violations happen.


Best

Kai-Uwe Bux
 
J

Jonathan Turkanis

Steven T. Hatton said:
In the following code, the class B will not compile if the function da is
uncommented. I took exactly the same code, and turned it into templates.
Never used the template parameter in anything but the baseclass name in the
derived class, and the code compiles without any complaints about protected
access. Why? What is different about the templated code?

Try compiling your code with this main function:

int main()
{
BT<int> b;
b.da(int());
}

You should get the same error. The reason the templated version
compiles in your original that dependent names (such as this->a)
aren't looked up until instantiation, and that no instantation occurs
in your example.

Jonathan
 
S

Steven T. Hatton

Victor said:
The templated code is not really "compiled" unless you attempt to
instantiate the template. Did you? Where is the rest of the code?
Was there anything else? If there wasn't, then all the compiler did
for your templates was checking the syntax. Names were not resolved,
access rights weren't checked, etc.

Yes, but your question gave me an idea I hadn't tried. I hadn't actually
invoke any functions on the template. When I did, the compiler rejected it.

int main() {
BT<int> bt(5);
bt.da(1); // comment this out and it compiles
}

g++ -omain2 main2.cc
main2.cc: In member function `BT<T>& BT<T>::da(const int&) [with T = int]':
main2.cc:51: instantiated from here
main2.cc:30: error: `int AT<int>::x' is protected
main2.cc:40: error: within this context
 
J

Jonathan Turkanis

Victor Bazarov said:
Steven T. Hatton wrote:

The templated code is not really "compiled" unless you attempt to
instantiate the template. Did you?

So is the dependent/non-dependent distinction a red herring here?
Where is the rest of the code?
Was there anything else? If there wasn't, then all the compiler did
for your templates was checking the syntax. Names were not resolved,
access rights weren't checked, etc.

Jonathan
 
J

Jonathan Turkanis

Victor Bazarov said:
Jonathan Turkanis wrote:

I am not sure I understand the question. Could you rephrase it?

Yes. I thought at first the reason access was not being checked was
that in the line

this->a.x += da_

the name a is qualified. But I see that Comeau compiles the code
wuthout the 'this->', as long as the template is not instantiated.

Jonathan
 
V

Victor Bazarov

Steven said:
Victor Bazarov wrote:




Yes, but your question gave me an idea I hadn't tried. I hadn't actually
invoke any functions on the template. When I did, the compiler rejected it.

int main() {
BT<int> bt(5);
bt.da(1); // comment this out and it compiles
}

Right. Instantiating a class template is not the same as instantiating
a member of a class template. If you don't call the function, it won't
be instantiated, and will not really be compiled (names resolved, access
checked, etc). So, to make sure a member of a template class is correct,
you got to try to use that member.

V
 
V

Victor Bazarov

Jonathan said:
Yes. I thought at first the reason access was not being checked was
that in the line

this->a.x += da_

the name a is qualified. But I see that Comeau compiles the code
wuthout the 'this->', as long as the template is not instantiated.

Aha. So, dependent/non-dependent distinction is not a red herring,
then, right?

V
 
J

Jonathan Turkanis

Victor Bazarov said:
Aha. So, dependent/non-dependent distinction is not a red herring,
then, right?

Comeau behaves the same with and without the qualification (which
makes sense, since I don't think the meaning of 'a' can change
depending on the template parameter T); that's why I said the
distinction was a red herring.

Jonathan
 
V

Victor Bazarov

Jonathan said:
Comeau behaves the same with and without the qualification (which
makes sense, since I don't think the meaning of 'a' can change
depending on the template parameter T); that's why I said the
distinction was a red herring.


OK, how about this:
--------------------------------
template<typename T>
class AT{
public:
AT(const int& x_=0): x(x_){}
protected:
int x;
};

template<typename T>
class BT: public AT<T>{
public:
BT(const int& x_=0): AT<T>(x_){}

BT& da(const int& da_)
{
a.x += da_;
return *this;
}

protected:
AT<T> a;
};

template<> class AT<int> {
public:
AT(int = 0) : x(0) {}
int x; /// Oops -- forgot 'protected'
};

int main() {
BT<double> btd;
btd.da(42); // error -- cannot access protected

BT<int> bti;
bti.da(42); // fine
}
------------------------------------------------
 
J

Jonathan Turkanis

Victor Bazarov said:
OK, how about this:
--------------------------------
template<typename T>
class AT{
public:
AT(const int& x_=0): x(x_){}
protected:
int x;
};

template<typename T>
class BT: public AT<T>{
public:
BT(const int& x_=0): AT<T>(x_){}

BT& da(const int& da_)
{
a.x += da_;
return *this;
}

protected:
AT<T> a;
};

template<> class AT<int> {
public:
AT(int = 0) : x(0) {}
int x; /// Oops -- forgot 'protected'
};

int main() {
BT<double> btd;
btd.da(42); // error -- cannot access protected

BT<int> bti;
bti.da(42); // fine
}
------------------------------------------------

Good. This shows why the access check has to be delayed, independent
of whether a is qualified.

Jonathan
 

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
474,262
Messages
2,571,056
Members
48,769
Latest member
Clifft

Latest Threads

Top