First, a disclaimer, this is something that I have learnt from other sources -- I can't remember where, except for the fact that I'd taken the source from the examples that it gave and used them in my own code. Please msg me about any formatting inconsistencies -- things like < and & need to be escaped to be valid HTML, and I may not have got all of them.
Ah Ariels -- if only it was that easy. Conversion to bool, as you have noted, is bad because we can do arithmetic on it. Which is not cool. But conversion to void * allows two things that we don't want:
Testable_By_Conversion t;
delete t; // damn! that sucks!
std::cout << t;
Yep, that's right, we can delete it, and we've got a surprise when the compiler implicitly converts it. The second one can be extended to any time when a void * can be used, which, if it happens deep in a template library, can be really hard to pick apart.
The fact that STL uses it is just because the correct technique hadn't been discovered at the time.
There are, in fact, two ways to do this, one is cleaner but loses on syntax, the other is perfect in terms of syntax, but the mechanism is enough to make even the greatest C++ gurus cross their legs and wince.
The first is to overload operator!, which means that the usual test for failure is then:
if(!testable) {
// do something when it fails...
and the test for "good":
if(!!testable)...
That's right kids! Double Bang! It's something that most people would not find comfortable doing, even though it is perhaps the best way to realise this feature. However, a library/component which uses an unknown idiom is itself useless.
So we get to try something else instead. The following code is what we need:
class Testable {
private:
typedef void (Testable::*bool_type)() const;
void __deep_dark_member_function() const {}
public:
// ... class stuff...
// et le piece de la resistance
operator bool_type () const {
if(/* internal test */) {
return &Testable::__deep_dark_member_function;
} else {
return 0;
}
}
// more class stuff ...
};
Is your head hurting yet?
For those who like nouns, this is a case where we return a member function pointer to a private member function. It happens that member function pointers can do even less than void *, and the fact that it's a private type means that there's no chance of namespace collision (technically, a very much reduced chance, where it wouldn't happen unless you were trying) -- there can't be any function that takes the returned value as a parameter. It also can't be delete'd, and it doesn't participate in arthimetic.
Actually, the code above is incomplete, since it still allows us to write silly things such as:
Testable t1, t2;
if(t1 == t2) { ...
This is dangerous if we had tried to disable comparisons between instances of Testable and then found that the compiler worked out a way to allow us to do it anyway... The solution is, as with the rest of this, sublime:
template <typename T>
bool operator==(const Testable & a, const T & b)
{
lhs.__deep_dark_member_function();
return false;
}
Note:we'd also have to overload operator!=, but I'm lazy
By having this as a non-member, non-friend function means that we can't actually access the deep and dark bits of Testable, and by having a template version, we know that it doesn't get generated by the compiler unless we actually try to use that comparison operator. Cool, huh? Further more, if we called the __deep_dark_member_function something useful, like this_class_does_not_support_comparisons, then the compiler would put that in the error message, and we'd effectively get diagnostic back out (this is also how the Boost concept check library does custom error messages)!
To wrap up then: this is the "correct way" to handle the case given. This is also painful, if you had to do it every time. Here, we see C++ flex its not-inconsiderable muscle. Yes, it's a pain to work out how it works, but the next bit ought to leave you amazed that this works at all. Basically, we're going to wrap this up into a neat little base class, so that classes that need to implement this, can just derive from the base class.
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 : 0;
}
protected:
virtual bool boolean_test() const=0;
virtual ~safe_bool() {}
};
template <typename T, typename U>
void operator==(const safe_bool& lhs,const safe_bool<U>& rhs) {
lhs.this_type_does_not_support_comparisons();
return false;
}
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 false;
}
This actually creates two versions that may be used. One is when you derive your class T from safe_bool<> (or, equivalently, safe_bool<void>), which means that you implement a virtual function called boolean_test, which contains the test. But a virtual function implies a vtable, which slows things down, etc, etc. So the other version is to derive T from safe_bool<T> (yes, you read that right, derived from a base that takes the current class as a parameter... see Curiously Recurring Template Pattern), this way, you define a non-virtual boolean_test, which is bound at compile time, since the compiler can now static_cast the function pointer.
Examples:
class T1: public safe_bool<> {
public:
virtual bool boolean_test() const {
// test goes here
}
}
class T2: public safe_bool<T2> {
public:
bool boolean_test() const {
// test goes here
}
}
So surprisingly, after all that, the final result is actually very clean and elegant. This is perhaps the best demonstration of the pain and the beauty that is in C++ -- yes, we had a difficult and archane journey, but the result is worth it -- a simple, easy to understand idiom that follows the Once And Only Once rule.
There are still a couple of variations possible. One is to use a pointer to member variable instead of function, because certain compilers (so I'm told) have difficulty in optimising the member function case; actually, some compilers have difficulting in applying EBO anyway, and most can't do it for the multiple inheritance case. The other variation is to derive the safe_bool bases as private instead, and bring the operator bool_type into the public area of the derived class with using clauses; this is safer in terms of keeping a clean namespace (especially if you then derive from the safe_bool child), but it means some typing, and nobody likes that.