User provided boolean conversion functions can cause more harm than benefit because it allows them to participate in expressions you would not ideally want them to. If a simple conversion operator is defined then two or more objects of unrelated classes can be compared. Type safety is compromised. For example,

structTestable{operatorbool()const{returnfalse;}};structAnotherTestable{operatorbool()const{returntrue;}};intmain(void){Testablea;AnotherTestableb;if(a==b){/* blah blah blah*/}if(a<0){/* blah blah blah*/}// The above comparisons are accidental and are not intended but the compiler happily compiles them.return0;}

Safe bool idiom allows syntactical convenience of testing using an intuitive if statement but at the same time prevents unintended statements unknowingly getting compiled in. Here is the code for the safe bool idiom.

classTestable{boolok_;typedefvoid(Testable::*bool_type)()const;voidthis_type_does_not_support_comparisons()const{}public:explicitTestable(boolb=true):ok_(b){}operatorbool_type()const{returnok_?&Testable::this_type_does_not_support_comparisons:0;}};template<typenameT>booloperator!=(constTestable&lhs,constT&){lhs.this_type_does_not_support_comparisons();returnfalse;}template<typenameT>booloperator==(constTestable&lhs,constT&){lhs.this_type_does_not_support_comparisons();returnfalse;}classAnotherTestable...// Identical to Testable.{};intmain(void){Testablet1;AnotherTestablet2;if(t1){}// Works as expectedif(t2==t1){}// Fails to compileif(t1<0){}// Fails to compilereturn0;}

Reusable Solution

There are two plausible solutions: Using a base class with a virtual function for the actual logic, or a base class that knows which function to call on the derived class. As virtual functions come at a cost (especially if the class you're augmenting with Boolean tests doesn't contain any other virtual functions). See both versions below:

classsafe_bool_base{public:typedefvoid(safe_bool_base::*bool_type)()const;voidthis_type_does_not_support_comparisons()const{}protected:safe_bool_base(){}safe_bool_base(constsafe_bool_base&){}safe_bool_base&operator=(constsafe_bool_base&){return*this;}~safe_bool_base(){}};// For testability without virtual function.template<typenameT=void>classsafe_bool:privatesafe_bool_base{// private or protected inheritance is very important here as it triggers the// access control violation in main.public:operatorbool_type()const{return(static_cast<constT*>(this))->boolean_test()?&safe_bool_base::this_type_does_not_support_comparisons:0;}protected:~safe_bool(){}};// For testability with a virtual function.template<>classsafe_bool<void>:privatesafe_bool_base{// private or protected inheritance is very important here as it triggers the// access control violation in main.public:operatorbool_type()const{returnboolean_test()?&safe_bool_base::this_type_does_not_support_comparisons:0;}protected:virtualboolboolean_test()const=0;virtual~safe_bool(){}};template<typenameT>booloperator==(constsafe_bool<T>&lhs,boolb){returnb==static_cast<bool>(lhs);}template<typenameT>booloperator==(boolb,constsafe_bool<T>&rhs){returnb==static_cast<bool>(rhs);}template<typenameT,typenameU>booloperator==(constsafe_bool<T>&lhs,constsafe_bool<U>&rhs){lhs.this_type_does_not_support_comparisons();returnfalse;}template<typenameT,typenameU>booloperator!=(constsafe_bool<T>&lhs,constsafe_bool<U>&rhs){lhs.this_type_does_not_support_comparisons();returnfalse;}

In C++, address of protected members functions can't be taken in a derived class. Derived class could be a standard class, a class template or a specialization of a class template. Some implementations of safe bool idiom declare safe_bool_base::this_type_does_not_support_comparisons as protected, address of which can't be taken in the derived class - a requirement in reusable safe bool idiom.