Weird compiler error and template inheritance - c++

Could someone explain me why this code:
class safe_bool_base
{ //13
protected:
typedef void (safe_bool_base::*bool_type)() const;
void this_type_does_not_support_comparisons() const {} //18
safe_bool_base() {}
safe_bool_base(const safe_bool_base&) {}
safe_bool_base& operator=(const safe_bool_base&) { return *this; }
~safe_bool_base() {}
};
template <typename T=void> class safe_bool : public safe_bool_base
{
public:
operator bool_type() const
{
return (static_cast<const T*>(this))->boolean_test() ? &safe_bool_base::this_type_does_not_support_comparisons : 0;
}
protected:
~safe_bool() {}
};
template <> class safe_bool<void> : public safe_bool_base
{
public:
operator bool_type() const
{
return (boolean_test() == true) ? &safe_bool_base::this_type_does_not_support_comparisons : 0; //46
}
protected:
virtual bool boolean_test() const = 0;
virtual ~safe_bool() {}
};
Produces the following compiler error ?
c:\project\include\safe_bool.hpp(46) : error C2248: 'safe_bool_base::this_type_does_not_support_comparisons' : cannot access protected member declared in class 'safe_bool_base'
c:\project\include\safe_bool.hpp(18) : see declaration of 'safe_bool_base::this_type_does_not_support_comparisons'
c:\project\include\safe_bool.hpp(13) : see declaration of 'safe_bool_base'
Since both safe_bool templates derive from safe_bool_base, I don't understand why one can't access a protected member of the base class.
Am I missing something ?

This should probably help (reproducible in a non template situation also)
struct A{
protected:
void f(){}
};
struct B : A{
void g(){&A::f;} // error, due to Standard rule quoted below
};
int main(){
}
VS gives "'A::f' : cannot access
protected member declared in class
'A'"
For the same code, Comeau gives
"ComeauTest.c", line 7: error:
protected function "A::f" (declared at
line 3) is
not accessible through a "A" pointer or object void g(){&A::f;}
^
"ComeauTest.c", line 7: warning:
expression has no effect void
g(){&A::f;}
Here is the fixed code which achieves the desired intentions
struct A{
protected:
void f(){}
};
struct B : A{
void g(){&B::f;} // works now
};
int main(){
}
So, why does the first code snippet not work?
This is because of the following rule in the C++ Standard03
11.5/1- "When a friend or a member function of a derived class references
a protected nonstatic member function
or protected nonstatic data member of
a base class, an access check applies
in addition to those described earlier
in clause 11.102) Except when forming
a pointer to member (5.3.1), the
access must be through a pointer to,
reference to, or object of the
derived class itself (or any class
derived from that class) (5.2.5). If
the access is to form a pointer to
member, the nested-name-specifier
shall name the derived class (or any
class derived from that class).
So change the return within operator functions as follows
return (boolean_test() == true) ? &safe_bool<void>::this_type_does_not_support_comparisons : 0; //46
return (static_cast<const T*>(this))->boolean_test() ? &typename safe_bool<T>::this_type_does_not_support_comparisons : 0;
EDIT 2: Please ignore my explanations. David is right. Here is what it boils down to.
struct A{
protected:
int x;
};
struct B : A{
void f();
};
struct C : B{};
struct D: A{ // not from 'C'
};
void B::f(){
x = 2; // it's own 'A' subobjects 'x'. Well-formed
B b;
b.x = 2; // access through B, well-formed
C c;
c.x = 2; // access in 'B' using 'C' which is derived from 'B', well-formed.
D d;
d.x = 2; // ill-formed. 'B' and 'D' unrelated even though 'A' is a common base
}
int main(){}

I don't think this is anything to do with templates. Your example code can be reduced to this, and it still gives the equivalent error:
class A
{
protected:
typedef void (A::*type)() const;
void foo() const {}
};
class B : public A
{
public:
operator type() const
{
return &A::foo;
}
};
I believe the issue is you can't return member-function pointers to protected members in the public interface. (Edit: not true...)

Chubsdad's answer clarifies your question of why there's an error for the template specialization.
Now the following C++ standard rule
14.7.2/11 The usual access checking rules do not apply to names used to specify explicit
instantiations. [Note: In particular, the template arguments and names used in the function
declarator (including parameter types, return types and exception specifications) may be
private types or objects which would normally not be accessible and the template may be a
member template or member function which would not normally be accessible. — endnote]
would explain why the generic template instantiation wouldn't throw an error. It will not throw even if you have private access specifier.

Related

why this definition of a derived class is illegal?

Why the derived class Derived_from_Private is illegal?
i noticed the member function has an reference to Base, but why it cannot have an reference to Base class?
class Base {
public:
void pub_mem(); // public member
protected:
int prot_mem; // protected member
private:
char priv_mem; // private member
};
struct Pub_Derv : public Base {
// legal
void memfcn(Base &b) { b = *this; }
};
struct Priv_Derv : private Base {
// legal
void memfcn(Base &b) { b = *this; }
};
struct Prot_Derv : protected Base {
// legal
void memfcn(Base &b) { b = *this; }
};
struct Derived_from_Public : public Pub_Derv {
// legal
void memfcn(Base &b) { b = *this; }
};
struct Derived_from_Private : public Priv_Derv {
// illegal
void memfcn(Base &b) { b = *this; }
};
struct Derived_from_Protected : public Prot_Derv {
// legal
void memfcn(Base &b) { b = *this; }
};
The expression
b = *this;
needs to invoke an implicit conversion from *this to an lvalue of type Base in order to call the implicitly declared Base::operator=(const Base&). This conversion goes through the path Derived_from_Private -> Priv_Derv -> Base. Since Priv_Derv has Base as a private base, Derived_from_Private does not have access to the second link.
Priv_Derv inherits privately Base. This means that only the class itself knows that it's also a Base and only the member functions of Priv_Derv can use members of Base.
You can later let Derived_from_Private inherit publicly from Priv_Derv. It's legal. But unfortunately, due to the former private inheritance, it's as if Derived_from_Private doesn't have Base as base class.
Therefore your member function will fail to compile:
void memfcn(Base &b) { b = *this; }
*this is a Derived_from_Private, but it's illegal to convert it to a Base class, because there is no known relation with that class due to the private inheritance.
Inheritance can provide both subtyping and structural extension.
When you inherits privately from a base class you have no subtyping, only structural extension. Then (in your problematic case) when you write b = *this alas *this is not of the type Base because you have used private inheritance of it.
Private inheritance is usually used (that doesn't mean it's a good practice) to easily construct very simple composition (has a base, but not is a base).
A name of a class is inserted into the scope of itself as a public member. This is so-called injected-class-name. Name lookup for Base in the derived class Derived_from_Private will find its injected-class-name instead of the normal one. Because the injected-class-name of Base is treated as a public member of Base, thus is treated as a private number of Priv_Derv, it is inaccessible in Derived_from_Private.
Quoted from [class.access.spec] paragraph 5:
[ Note: In a derived class, the lookup of a base class name will find the injected-class-name instead of the name of the base class in the scope in which it was declared. The injected-class-name might be less accessible than the name of the base class in the scope in which it was declared. — end note ] [ Example:
class A { };
class B : private A { };
class C : public B {
A* p; // error: injected-class-name A is inaccessible
::A* q; // OK
};
— end example ]

Protected member access error in template specialization

class safe_bool_base {
protected:
void this_type_does_not_support_comparisons() const {}
};
template <typename T=void> class safe_bool : public safe_bool_base {
public:
void func() {
&safe_bool::this_type_does_not_support_comparisons;
&safe_bool_base::this_type_does_not_support_comparisons;
}
};
template<> class safe_bool<void> : public safe_bool_base {
public:
void func() {
&safe_bool::this_type_does_not_support_comparisons;
&safe_bool_base::this_type_does_not_support_comparisons;
}
};
Error Message:
zzz.cpp: In member function 'void safe_bool<void>::func()':
zzz.cpp:7:10: error: 'void safe_bool_base::this_type_does_not_support_comparison
s() const' is protected
void this_type_does_not_support_comparisons() const {}
^
zzz.cpp:22:24: error: within this context
&safe_bool_base::this_type_does_not_support_comparisons;
^
I wonder why protected member can not be visited in the template specialization. The codes are meaningless and just for testing.
When public inherite from the base class, its protected members become the derived class' protect members, which could be accessed in derived class' member functions. Note they're only accessed through the derived class itself (and its derived classes). But the protected members can't be accessed through the base class. That why &safe_bool::this_type_does_not_support_comparisons; works but &safe_bool_base::this_type_does_not_support_comparisons; doesn't.
From the standard, 11.4/1 Protected member access
[class.protected]:
(emphasie mine)
An additional access check beyond those described earlier in Clause
[class.access] is applied when a non-static data member or non-static
member function is a protected member of its naming class
([class.access.base])114 As described earlier, access to a protected
member is granted because the reference occurs in a friend or member
of some class C. If the access is to form a pointer to member
([expr.unary.op]), the nested-name-specifier shall denote C or a class
derived from C. All other accesses involve a (possibly implicit)
object expression ([expr.ref]). In this case, the class of the object
expression shall be C or a class derived from C. [ Example:
class B {
protected:
int i;
static int j;
};
class D1 : public B {
};
class D2 : public B {
friend void fr(B*,D1*,D2*);
void mem(B*,D1*);
};
...
void D2::mem(B* pb, D1* p1) {
pb->i = 1; // ill-formed
p1->i = 2; // ill-formed
i = 3; // OK (access through this)
B::i = 4; // OK (access through this, qualification ignored)
int B::* pmi_B = &B::i; // ill-formed
int B::* pmi_B2 = &D2::i; // OK
j = 5; // OK (because j refers to static member)
B::j = 6; // OK (because B::j refers to static member)
}
...
— end example ]
Note the statement int B::* pmi_B = &B::i; // ill-formed in the sample code from the standard, basically it's the same case of your code. BTW it has nothing to do with template specialization.

Policy inheritance and inaccessible protected members

It seems that a protected member from a template policy class is inaccessible, even with a class hierarchy which seems correct.
For instance, with the following code snippet :
#include <iostream>
using namespace std;
template <class T>
class A {
protected:
T value;
T getValue() { return value; }
public:
A(T value) { this->value = value; }
};
template <class T, template <class U> class A>
class B : protected A<T> {
public:
B() : A<T>(0) { /* Fake value */ }
void print(A<T>& input) {
cout << input.getValue() << endl;
}
};
int main(int argc, char *argv[]) {
B<int, A> b;
A<int> a(42);
b.print(a);
}
The compiler (clang on OS X, but gcc returns the same type of error) returns the following error :
Untitled.cpp:18:21: error: 'getValue' is a protected member of 'A<int>'
cout << input.getValue() << endl;
^
Untitled.cpp:25:5: note: in instantiation of member function 'B<int, A>::print' requested here
b.print(a);
^
Untitled.cpp:8:7: note: can only access this member on an object of type 'B<int, A>'
T getValue() { return value; }
^
1 error generated.
The strange thing is that the last note from the compiler is totally correct but already applied since the b object is of type 'B<int, A>'. Is that a compiler bug or is there a problem in the code ?
Thanks
You have misunderstood the meaning of protected access.
Protected members are callable by derived classes. But only on the base object contained inside the class itself.
For example, if i simplify the problem, using :
class A {
protected:
void getValue(){}
};
class B : protected A
{
public:
void print(A& input)
{
input.getValue(); //Invallid
}
};
getValue cannot be called on a "A" object other than the "A" object inside the class itself.
This for example is valid.
void print()
{
getValue(); //Valid, calling the base class getValue()
}
As pointed out by Dan Nissenbaum and shakurov. This is however also valid:
void print(B& input)
{
input.getValue();
}
This is because we explicitly say that input is a object of B. And the compiler know that all that objects of B has protected access to getValue. In the case when we pass a A&, the object might as-well be a type of C, wich could be derrived from A with private access.
Let's forget for a minute about the template and look at this:
class A {
protected:
int value;
int getValue() { return value; }
public:
A(int value) { this->value = value; }
};
class B : protected A {
public:
B() : A(0) { /* Fake value */ }
void print(A& input) {
cout << input.getValue() << endl;
}
};
The print() method's implementation is wrong because you can't access non-public member of A inside B. And here's why: from within B, you can only access non-public members of B. Those members may be either inherited or not — it doesn't matter.
On the other hand, A& input may not be a reference to an instance of B. It may be a reference to another subclass (which may well have getValue() inaccessible).
Member functions of a derived class have access to protected base class members within any object of its type that is passed as an argument so long as the explicitly declared class of the object passed as an argument is that of the the derived class (or a further derived class).
Objects explicitly passed as the base class type cannot have their protected members accessed within the derived class's member functions.
In other words, if we have:
class A
{
protected:
int x;
}
class B : public A
{
void foo(B b)
{
b.x; // allowed because 'b' is explicitly declared as an object of class B
}
void goo(A a)
{
a.x; // error because 'a' is explicitly declared as having *base* class type
}
};
...then the line a.x is not allowed because the explicit type of the argument is A, but the rule for protected access only applies to objects explicitly defined as the same class as the class attempting to access the member. (...Or a class derived from it; i.e., if class Cderives from B, then passing an object explicitly declared as an object of class C will also have x accessible within B member functions.)
The reason for this is given by shakurov when he writes (paraphrasing)
A& input might not be a reference to an instance of B. It may be a
reference to another subclass (which may well have getValue()
inaccessible)
An excellent explication of this answer is also given here: accessing a protected member of a base class in another subclass.
As a matter of interest, I believe that this comes from the C++ standard here:
11.4 Protected member access [class.protected] 1 An additional access check beyond those described earlier in Clause 11 is applied when a
non-static data member or non-static member function is a protected
member of its naming class (11.2)115 As described earlier, access to a
protected member is granted because the reference occurs in a friend
or member of some class C. If the access is to form a pointer to
member (5.3.1), the nested-name-specifier shall denote C or a class
derived from C. All other accesses involve a (possibly implicit)
object expression (5.2.5). In this case, the class of the object
expression shall be C or a class derived from C.
Don't get distracted by the template. It has nothing to do with the error. The line in main that the compiler is complaining about creates an object of type B<int, a> and tries to access a protected member. That's not legal, regardless of the type. You can only use protected members from inside a member function or friend function. For example:
struct S {
protected:
void f();
};
int main() {
S s;
s.f(); // error: attempts to call a protected member function
}

Calling protected base class method via this pointer casted to base class in derived class (C++)

To begin with, I know about C++ Standard (ISO/IEC 14882:2003): Section 11.5, Paragraph 1, and this is not that case (but compliler apparently does not think so).
I try to call protected base class method in derived class method through this pointer, static-casted to base class pointer and have in MSVC2008 error C2248: 'A::f' : cannot access protected member declared in class 'A'.
I have to do this in context of 'curiously recurring template pattern', but I can reproduce this error in simplier code, as follows:
class B
{
protected:
void f(){}
};
class D : public B
{
public:
void g()
{
f(); // ok
this->f(); // ok
static_cast<B*>(this)->f(); // C2248 in MSVC2008
dynamic_cast<B*>(this)->f(); // C2248
((B*)this)->f(); // C2248
}
};
D d; d.g();
It seems that compiler think of casted this pointer as a pointer to other instance, yeah?
The compiler is wrong in this case, what do you think?
Ok, my real code is more like that:
template<class T>
class B
{
public:
void g()
{
f(); // error C3861: 'f': identifier not found
this->f(); // error C3861: 'f': identifier not found
// static_cast to derived class
static_cast<T*>(this)->f(); // C2248 in MSVC2008
}
};
class D : public B<D>
{
protected:
void f(){}
};
I cast this to derived class, and I can't use this->f();
By the way, I see that this code is unsafe for usage like class E : public B<D> {...};: compilable, but static_cast makes wrong cast.
The compiler is correct. To explicitly access the B::f member function, you can write:
this->B::f();
The relevant language is:
c++11
11.4 Protected member access [class.protected]
[...] Access to a protected member is granted because the reference occurs in a friend or member of some
class C. [...] Access to a protected member [...] involve[s] a (possibly implicit) object expression (5.2.5). In this case,
the class of the object expression shall be C or a class derived from C.
Thus protected member access via a cast to the base class B violates this grant, and is disallowed. It is also unnecessary for the reason that you can use this->B::f() as above.
In the case with your actual CRTP motivation, you are correct that you cannot call f() without a static_cast, since D is not a base class of B<D> (the inheritance relationship is in the other direction). Since D is not a base class of B<D>, you cannot call its protected methods from B<D> anyway. One simple workaround is to friend B<D> to D and use the static_cast on the this pointer:
template<typename T>
class B {
public:
void g() {
static_cast<T *>(this)->f();
}
};
class D : public B<D>
{
friend class B<D>;
...
If giving B access to the private parts of D worries you, you can move the private parts to another base class and isolate the CRTP mechanism in D:
template<class T> class B {
public:
void g() {
static_cast<T*>(this)->f();
}
};
class C {
private:
void h();
protected:
void f(){ std::cout << "D::f\n"; }
};
class D: protected C, public B<D>
{
friend class B<D>;
};
Here B<D> is prevented from calling C::h as friendship is neither inherited nor transitive.
I think the compiler is right.
Suppose the following:
void g()
{
B *b1 = this;
B *b2 = GetUnrelatedB();
b1->f(); //Error?
b2->f(); //Error!
}
The b1 case is equivalent to your static_cast but it would be very strange that b1 will be allowed and b2 will not.
Citing your paragraph 11.5:
[...] the access must be through a pointer to, reference to, or object of the derived class itself.
But static_cast<B*>(this) is of type B*, not D*, no matter that the object itself is the same. Actually, the value of the pointer is irrelevant to this issue, only the type of the expression:
void g()
{
B *b2 = GetUnrelatedB();
static_cast<D*>(b2)->f(); //ok!
}
But, how would the compiler know that you are inside a class derived from B once you apply static_cast on this? In my (humble) opinion, if I create a B object, I expect not to be allowed to call private or protected B methods on the B object, since we don't want to violate encapsulation. It would not matter where the B object is created, as long as it's outside of the B class methods.

Protected data in parent class not available in child class?

I am confused: I thought protected data was read/writable by the children of a given class in C++.
The below snippet fails to compile in MS Compiler
class A
{
protected:
int data;
};
class B : public A
{
public:
B(A &a)
{
data = a.data;
}
};
int main()
{
A a;
B b = a;
return 0;
}
Error Message:
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
demoFail.cpp
demoFail.cpp(12) : error C2248: 'A::data' : cannot access protected member declared in class 'A'
demoFail.cpp(4) : see declaration of 'A::data'
demoFail.cpp(2) : see declaration of 'A'
What am I doing wrong?
According to TC++PL, pg 404:
A derived class can access a base class’ protected members only for objects of its own type.... This prevents subtle errors that would otherwise occur when one derived class corrupts data belonging to other derived classes.
Of course, here's an easy way to fix this for your case:
class A
{
protected:
int data;
};
class B : public A
{
public:
B(const A &a)
: A(a)
{
}
};
int main()
{
A a;
B b = a;
return 0;
}
The C++ Standard says about protected non-static members at 11.5/1
When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11. Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).
In addition to fixing things mentioned earlier by others (constructor of B is private), i think rlbond's way will do it fine. However, a direct consequence of the above paragraph of the Standard is that the following is possible using a member pointer, which arguably is a hole in the type system, of course
class B : public A {
public:
B(A &a){
int A::*dataptr = &B::data;
data = a.*dataptr;
}
};
Of course, this code is not recommended to do, but shows that you can access it, if you really need to (I've seen this way being used for printing out a std::stack, std::queue, std::priority_queue by accessing its protected container member c)
You just shouldn't copy an A object in a B constructor. The intention is to leave the initialization of A's members to it's own constructor:
struct A {
A( const A& a ): data( a.data ) {}
protected: int data;
};
struct B : public A {
B( const A& a ): A( a ) {}
};
The constructor of B is private. If you do not specify anything, in a class, the default modifier is private (for structs it is public). So in this example the problem is that you cannot construct B. When you add public to constructor B anoter problem arises:
B has the right to modify the part of A it derives from but not another A like in this case.
You could do following:
class A
{
public:
A()
: data(0)
{
}
A(A &a)
{
data = a.data;
}
protected:
int data;
};
class B : public A
{
public:
B(A &a)
: A(a)
{
}
};
int main()
{
A a;
B b = a;
return 0;
}