Smooth Operator
Let's analyze the
return statement. If
rawptr isn't
NULL, the conversion operator returns a pointer to a member of struct
PtrConversion (the precise value of the expression
&PtrConversion::valid is immaterial; what matters is that it's a non-
NULL constant). However, if
rawptr is
NULL, the conversion operator returns a
NULL pmi. Recall that all pointers to members are implicitly convertible to
bool; they aren't
bool variables, though. This subtle difference is crucial, as you will see.
After this change, when the following statement executes:
if(ptr)
//...
C++ calls
ptr.operator pmi() behind the scenes. The return value of type
pmi is then silently converted to
bool, as expected. However, the following statement no longer compiles, which is exactly what you want:
cout<<"p1 + p2 = "<< p1+p2 <<endl; //compilation error
Here's the secret: C++ guarantees that an operand shall undergo at most one implicit conversion. In the
if(ptr) example, you have exactly one implicit conversion from
pmi to
bool (the very invocation of a conversion operator doesn't constitute an implicit conversion for this matter). However, in the expression
p1+p2 two implicit conversions per operand are necessary:
- pmi>bool
- bool>int
As I said, C++ doesn't allow this.
Let's see how this technique eliminates the problems caused by a void* conversion operator:
cout << pf <<endl; //now a compilation error
This statement no longer compiles since there is no overloaded version of
operator << that takes
pmi or
Ptr<T>. Similarly, the following
delete expression:
delete pf; //compilation error
doesn't compile because neither
pmi nor
bool are valid types in a
delete expression.
Twisting by the bool
The boost::shared_ptr class uses a slightly different version of the indirect conversion technique. Instead of using a dummy nested struct, it uses a pointer to a member of class shared_ptr itself. In terms of memory footprint, there is no difference between the two versions because the dummy struct is not instantiated anyway. However, some compilers have difficulties coping with pointers to member functions of a class template so the Boost library uses conditional compilation to switch between a pointer to a data member and a pointer to a member function as the target type of the Boolean conversion operator. The bottom line is this: instead of bool, void* or int as the target type of a Boolean conversion operator, use a pointer to member to minimize the perils of implicit conversions.