Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2001-09-20 14:15:18

On Wednesday 19 September 2001 02:08, you wrote:
> I've been playing with the safe_bool technique -taken from
> function_base.hpp- and I've found the following:
> I've used the following test bed.
> In this test there is a class A with either 'operator bool()' or 'operator
> safe_bool()'.
> I can switch between operators by commenting out one of them.
> Following the class declaration there is a set of test cases. Each test is
> contained within one of two macros:
> I can switch between macros by commenting out one of them.
> With operator bool(), all test passed.
> With operator safe_bool(), only the tests that are inside PASSED() in the
> code below passed.
> file://FAILED(
> bool compare_to_const_int ()
> {
> const int null = 0 ;
> A a ;
> return a == null ;
> }
> )

I'm not sure what compiler you used, but this should fail (and does, on both
GCC 3.0 and Comeau in their strictest modes).

> As you can see, all conversions to bool were accepted, except the one
> required to allow the explicit comparison with a bool value.
> Notice that a conventional 'operator bool()' also accepts all the int
> conversions because of the allowed promotion from bool to int.


> Notice also that the comparison to the literal 0 (not literal 1) and the
> comparison to a const int (with value 0) are allowed.

Literal 0 compares, const int (with value zero) should not.

> Of course, this is the expected behavior, otherwise the expressions if ( A
> ) / if ( A == 0 ) wouldn't compile.

"if (A)" is definitely desirable, though I'm not sure if "if (A == 0)" is

> safe_bool is a pointer (to a member function), thus it is allowed to
> compare with a null pointer value and it is allowed to decay into bool.
> If I change safe_bool from a member function pointer into a regular pointer
> (int const*), all the same tests passed except 'compare_with_const_int'
> (pretty odd).
> It all means that safe_bool is safer than 'operator bool()', but not safer
> than 'operator T const*' (when the later is applicable).

I disagree with this. Peter Dimov chose to use a member pointer specifically
because they do not allow pointer arithmetic or ordering. If one uses a
conversion to a pointer, expressions such as "A + 2", "A++", and "A < B" must
also be poisoned.

> If safe_bool is going to be added to utility.hpp I think these results
> should be documented.

All possible side effects will be documented.

> In the particular case of optional<T>, the allowed conversion to bool
> prevents this technique to be applicable.

I'm not sure I understand this, but I'm also not familiar with optional<T>.

> It seems to me that (!,!!) and get(opt) are still the only truly safe
> choices. (and then the poison ==,!= are not required)

I think there are safe choices. Just be using the member pointer we narrow
the set of operations to:

a == b
a != b
a == 0
a != 0
0 == a
0 != a

The first two are clearly a problem, and should be poisoned. The other four
may or may not be problem, but can likewise be poisoned. A bool-convertible
class Foo would be defined as:

class Foo : boost::bool_convertible<Foo> {
  operator safebool() const { /* .. */ }
  safebool operator!() const { /* ... */ }


template<typename T>
class bool_convertible {
  friend void operator==(const Foo&, const Foo&);
  friend void operator!=(const Foo&, const Foo&);
  friend void operator==(const int, const Foo&);
  friend void operator!=(const int, const Foo&);
  friend void operator==(const Foo&, const int);
  friend void operator!=(const Foo&, const int);


Boost list run by bdawes at, gregod at, cpdaniel at, john at