Safe bool idiom - protected function not accessible

M

mlimber

In an article on the safe bool idiom
(http://www.artima.com/cppsource/safeboolP.html), Bjorn Karlsson gives
the following code (slightly modified):

class safe_bool_base {
protected:
typedef void (safe_bool_base::*bool_type)() const;
void this_type_does_not_support_comparisons() const {}

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 //Error!
: 0;
}
protected:
virtual bool boolean_test() const=0;
virtual ~safe_bool() {}
};
template <typename T, typename U>
void operator==(const safe_bool<T>& lhs,const safe_bool<U>& rhs) {
lhs.this_type_does_not_support_comparisons();
return;
}

template <typename T,typename U>
void operator!=(const safe_bool<T>& lhs,const safe_bool<U>& rhs) {
lhs.this_type_does_not_support_comparisons();
return;
}

class Testable_with_virtual : public safe_bool<> {
protected:
bool boolean_test() const {
return false;
}
};

class Testable_without_virtual :
public safe_bool <Testable_without_virtual> {
public:
bool boolean_test() const {
return true;
}
};

This does not compile with VC8, VC7, or Comeau, but it does with EDG,
GNU, and VC6 (slightly modified further to account for
non-conformancies with void template args). The error occurs on the
line indicated by "Error!" and on Comeau reads:

"ComeauTest.c", line 26: error: protected function
"safe_bool_base::this_type_does_not_support_comparisons" (declared at
line 4) is not accessible through a "safe_bool_base" pointer or object
&safe_bool_base::this_type_does_not_support_comparisons //Error!
^

Notice that only the class template's specialization has a problem. Is
Comeau right as usual?

Cheers! --M
 
G

Greg

mlimber said:
In an article on the safe bool idiom
(http://www.artima.com/cppsource/safeboolP.html), Bjorn Karlsson gives
the following code (slightly modified):

class safe_bool_base {
protected:
typedef void (safe_bool_base::*bool_type)() const;
void this_type_does_not_support_comparisons() const {}

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 //Error!
: 0;
}
protected:
virtual bool boolean_test() const=0;
virtual ~safe_bool() {}
};

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Greg
 
M

mlimber

Greg said:
Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Cheers! --M
 
G

Greg

mlimber said:
Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

Greg
 
M

mlimber

Greg said:
mlimber said:
Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

Cheers! --M
 
G

Greg

mlimber said:
Greg said:
mlimber said:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression. The only protected members that a
class has that kind of access are its own protected members and the
protected members of classes that derive from it. And that rule is in
fact the reason why changing the class of the member pointer from
safe_bool_base to safe_bool eliminates the access violation.

Therefore every member pointer above that refers to a protected member
of a base class - is an access violation waiting to happen. The actual
error is deferred of course until the code in question is instantiated.
So there is nothing about the void specialization that is any different
than the others templates - they all have the same problem.

Greg
 
M

mlimber

Greg said:
mlimber said:
Greg said:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?
The only protected members that a
class has that kind of access are its own protected members and the
protected members of classes that derive from it.

Huh? A class can obtain a pointer to a member of a class that derives
from it? Unless you're talking only about virtual functions, you've
lost me completely.
And that rule is in
fact the reason why changing the class of the member pointer from
safe_bool_base to safe_bool eliminates the access violation.

Therefore every member pointer above that refers to a protected member
of a base class - is an access violation waiting to happen. The actual
error is deferred of course until the code in question is instantiated.
So there is nothing about the void specialization that is any different
than the others templates - they all have the same problem.

But the code given in my OP *does* instantiate both the specialized and
non-specialized versions, but only the specialized version causes the
compiler to choke.

Cheers! --M
 
M

mlimber

Greg said:
mlimber said:
Greg said:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?
The only protected members that a
class has that kind of access are its own protected members and the
protected members of classes that derive from it.

Huh? A class can obtain a pointer to a member of a class that derives
from it? Unless you're talking only about virtual functions, you've
lost me completely.
And that rule is in
fact the reason why changing the class of the member pointer from
safe_bool_base to safe_bool eliminates the access violation.

Therefore every member pointer above that refers to a protected member
of a base class - is an access violation waiting to happen. The actual
error is deferred of course until the code in question is instantiated.
So there is nothing about the void specialization that is any different
than the others templates - they all have the same problem.

But the code given in my OP *does* instantiate both the specialized and
non-specialized versions, but only the specialized version causes the
compiler to choke.

Cheers! --M
 
M

mlimber

Greg said:
mlimber said:
Greg said:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?
The only protected members that a
class has that kind of access are its own protected members and the
protected members of classes that derive from it.

Huh? A class can obtain a pointer to a member of a class that derives
from it? Unless you're talking only about virtual functions, you've
lost me completely.
And that rule is in
fact the reason why changing the class of the member pointer from
safe_bool_base to safe_bool eliminates the access violation.

Therefore every member pointer above that refers to a protected member
of a base class - is an access violation waiting to happen. The actual
error is deferred of course until the code in question is instantiated.
So there is nothing about the void specialization that is any different
than the others templates - they all have the same problem.

But the code given in my OP *does* instantiate both the specialized and
non-specialized versions, but only the specialized version causes the
compiler to choke.

Cheers! --M
 
M

mlimber

Greg said:
mlimber said:
Greg said:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?
The only protected members that a
class has that kind of access are its own protected members and the
protected members of classes that derive from it.

Huh? A class can obtain a pointer to a member of a class that derives
from it? Unless you're talking only about virtual functions, you've
lost me completely.
And that rule is in
fact the reason why changing the class of the member pointer from
safe_bool_base to safe_bool eliminates the access violation.

Therefore every member pointer above that refers to a protected member
of a base class - is an access violation waiting to happen. The actual
error is deferred of course until the code in question is instantiated.
So there is nothing about the void specialization that is any different
than the others templates - they all have the same problem.

But the code given in my OP *does* instantiate both the specialized and
non-specialized versions, but only the specialized version causes the
compiler to choke.

Cheers! --M
 
M

mlimber

Greg said:
mlimber said:
Greg said:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?
The only protected members that a
class has that kind of access are its own protected members and the
protected members of classes that derive from it.

Huh? A class can obtain a pointer to a member of a class that derives
from it? Unless you're talking only about virtual functions, you've
lost me completely.
And that rule is in
fact the reason why changing the class of the member pointer from
safe_bool_base to safe_bool eliminates the access violation.

Therefore every member pointer above that refers to a protected member
of a base class - is an access violation waiting to happen. The actual
error is deferred of course until the code in question is instantiated.
So there is nothing about the void specialization that is any different
than the others templates - they all have the same problem.

But the code given in my OP *does* instantiate both the specialized and
non-specialized versions, but only the specialized version causes the
compiler to choke.

Cheers! --M
 
M

mlimber

Greg said:
mlimber said:
Greg said:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?
The only protected members that a
class has that kind of access are its own protected members and the
protected members of classes that derive from it.

Huh? A class can obtain a pointer to a member of a class that derives
from it? Unless you're talking only about virtual functions, you've
lost me completely.
And that rule is in
fact the reason why changing the class of the member pointer from
safe_bool_base to safe_bool eliminates the access violation.

Therefore every member pointer above that refers to a protected member
of a base class - is an access violation waiting to happen. The actual
error is deferred of course until the code in question is instantiated.
So there is nothing about the void specialization that is any different
than the others templates - they all have the same problem.

But the code given in my OP *does* instantiate both the specialized and
non-specialized versions, but only the specialized version causes the
compiler to choke.

Cheers! --M
 
G

Greg

mlimber said:
Greg said:
mlimber said:
Greg wrote:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?

§11.5 is the relevant section of the Standard:

"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 name 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."

Of course the members of an object include those members (if any) that
it inherits from any of its base classes; and for this reason we think
of "protected" access as allowing access to a class's members by its
derived classes - but strictly speaking, that view is not completely
accurate because the access to the member is always indirect - through
the derived class member - and not a direct access to the protected
member of the base class - as the error in the above program makes
clear.

Greg
 
M

mlimber

Greg said:
mlimber said:
Greg said:
mlimber wrote:
Greg wrote:
mlimber wrote:
Greg wrote:

Change the line with the error to:

return boolean_test()==true ?
&safe_bool::this_type_does_not_support_comparisons
: 0; // OK

and the error should be fixed.

Indeed. Now the question is, Why could the normal template class get to
that member but the explicit specialization could not?

Neither one has access:

int main()
{
safe_bool<int> sb;
}

error: 'safe_bool<T>::~safe_bool() [with T = int]' is protected

The destructor is protected, indicating that safe_bool should only be
used as a base class. The derived classes should have full access to
the protected members of the base class, but it seems that only the
non-specialized derived class does. Any idea why (or is this one of the
rare bugs in Comeau)?

No, Comeau is correct: a derived class does not have access to
protected members of a base class - either for the purposes of
obtaining a member pointer to one or even to use the protected member
in any kind of object expression.

Can you cite the standard in this regard?

§11.5 is the relevant section of the Standard:

"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 name 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."

Of course the members of an object include those members (if any) that
it inherits from any of its base classes; and for this reason we think
of "protected" access as allowing access to a class's members by its
derived classes - but strictly speaking, that view is not completely
accurate because the access to the member is always indirect - through
the derived class member - and not a direct access to the protected
member of the base class - as the error in the above program makes
clear.

Greg

Ok, I get it now. Thanks for your help. BTW, the error does show up in
both versions if they are instantiated:

int main()
{
Testable_with_virtual twv;
Testable_without_virtual twov;
if( twv && twov ) return 1;
return 0;
}

Cheers! --M
 

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

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top