views:

149

answers:

3

I was looking into how std::tr1::shared_ptr<> provides the ability to cast to bool. I've got caught out in the past when trying to create a smart pointer that can be casted to bool as the trivial solution, ie

operator bool() {
  return m_Ptr!=0;
}

usually ends up being implicitly castable to the pointer type (presumably by type promotion), which is generally undesirable. Both the boost and Microsoft implementations appear to use a trick involving casting to an unspecified_bool_type(). Can anyone explain how this mechanism works and how it prevents implicit casting to the underlying pointer type?

+3  A: 

Usually what it returns is a member pointer. Member pointers can be treated like a bool but don't support many of the implicit conversions which bool does.

sbi
+13  A: 

It uses the safe bool idiom. It provides an implicit conversion to a member-function-pointer which can be used in a condition, but nowhere else.


If you're interested, here's how the issue will be handled in C++0x.

Joe Gauterin
+1: Very nice answer.
ereOn
Oh, I knew of the safe bool idiom and recently discovered the `explicit` conversion operators, but I didn't know about this lil' `null_ptr` thingy. Thanks :)
Matthieu M.
+2  A: 

The trick works like this. You define all this inside your smart pointer type (in this case, shared_ptr):

private:

  struct Tester
  {
    Tester(int) {}  // No default constructor
    void dummy() {}
  };

  typedef void (Tester::*unspecified_bool_type)();

public:

  operator unspecified_bool_type() const
  {
    return !ptr_ ? 0 : &Tester::dummy;
  }

ptr_ is the native pointer inside the smart pointer class.

As you can see, unspecified_bool_type is a typedef to a type that cannot be accessed by any external code, since Tester is a private struct. But calling code can use this (implicit) conversion to a pointer type and check whether it is null or not. Which, in C++, can be used as a bool expression.

Gorpik
What's up with the `##ModelId`?
KennyTM
@KennyTM: Sorry, just removed that.
Gorpik
You need to provide implementations of `!=` and `==` for `Tester::unspecified_bool_type` or you'll allow meaningless comparissons between instances of the class to compile.
Joe Gauterin
@Joe Gauterin: Why? `Tester::unspecified_bool_type` is just a pointer (a pointer to a function, in fact), you don't need to define such a thing. And there is no problem in comparing those pointers; after all, the comparisons behave as expected.
Gorpik
@Gorpik: If you `==` compare two instances of a class using the idiom, they'll both implicitly convert to the `unspecified_bool_type`, the comparrison will return whether they both have the same conversion to `unspecified_bool_type`, not whether they are equal.
Joe Gauterin
@Joe Gauterin: Then you mean defining `!=` and `==` for the smart pointer class, not for `unspecified_bool_type`. Of course, there are lots of additional things you have to do for the smart pointer to work correctly. Specifically, you have to redefine all operators that would work with raw pointers.
Gorpik
No, I mean that for a class to use the safe bool idiom without otherwise altering the behaviour of the class, the `==` and `!=` operators must be defined for the `unspecified_bool_type`.
Joe Gauterin
But @Joe, the same would happen for *any* implicit conversion operators on the smart-pointer class. This doesn't seem to be anything particular to the special bool type concocted here. If you use `==` to compare smart-pointer objects, and the only way the compiler can satisfy that request is to call the implicit conversion operators and compare the results, what's the harm? Besides, how do you suggest the member-pointer comparison operators be implemented?
Rob Kennedy
@Rob: Yes, it would happen for any implicit conversion - which is why implicit conversion operators are generally avoided. The harm created by enabling operator `==` accidentally is that an error previously detectable by the compiler becomes a hard to detect runtime error. See the link in my answer for suggested implementation.
Joe Gauterin
@Joe Gauterin: If you redefine operators `==` and `!=` for the smart pointer class (in addition to all other operators that work on raw pointers, such as `*` and `->`), you have no problems at all. Comparing two smart pointers will correctly call the comparison operator with no implicit conversions to `unspecified_bool_type` at all.
Gorpik
@Gorpik: I agree, but the safe bool idiom is useful on things other than smart pointers, so it's important to be aware of that issue.
Joe Gauterin