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(){}