Although this is old, the safe bool idiom has its historical significance and I decide to write a full answer because I really disagree with the accepted answer.
One important thing is that we cannot answer this question in isolation. The function is called this_type_does_not_support_comparisons
for a reason. It is not just some random private member. The intention is clear: the function is also used to avoid accidental comparison between two testables. If for example we look at the implementation here, we can see that the function is actually called.
class Testable {
bool ok_;
typedef void (Testable::*bool_type)() const;
void this_type_does_not_support_comparisons() const {}
public:
explicit Testable(bool b=true):ok_(b) {}
operator bool_type() const {
return ok_ ?
&Testable::this_type_does_not_support_comparisons : 0;
}
};
template <typename T>
bool operator!=(const Testable& lhs, const T&) {
lhs.this_type_does_not_support_comparisons();
return false;
}
template <typename T>
bool operator==(const Testable& lhs, const T&) {
lhs.this_type_does_not_support_comparisons();
return false;
}
Note first that this code will not compile on modern compilers because the body of the function is unconditionally ill-formed no matter what T
is. However, let's consider it from a historical perspective. In older c++
versions this ensures that when operator==
is called on two testables, and the comparison message nicely indicates the real issue. For example, GCC 4.1.2 generates the following error message.
error: 'void
Testable::this_type_does_not_support_comparisons() const' is private
This is why the function is called this_type_does_not_support_comparison
in the first place. The const
need to be there because the function is actually called in a context where const
needed, namely template <typename T> bool operator==(const Testable& lhs, const T&)
. Note that const
cannot be removed from the first parameter because otherwise it cannot detect the comparison between a const object and another object and silent conversion to bool
becomes possible again.
So here are my answers to the questions,
- How come the
bool_type
(the typedef) and this_type_does_not_support_comparisons
are const?
Because the function is actually called and the const
s are necessary to suppress the implicit conversion to bool in the context of operator==
. It's definitely not because someone did not understand what they wrote or to prevent the testables to be called with the (testable.*testable)()
syntax (That is already a very exotic use and no one uses a bool like that, so that should not be considered an accidental misuse to be prevented at all. Moreover, if you try it, it doesn't actually compile).
- Nobody is supposed to actually call the member function through the return pointer anyway, right?
Mostly right. The member functions are not supposed to be called from outside but are actually called in the comparison operators from the class itself. It is easy to miss this use because it is not included in the original snippet of the question.
Again, no if we limit ourselves to the snippet posted, yes if we consider the broader context of how the canonical safe bool idiom is implemented.
- Would
operator bool_type
(the member function) violate const-correctness otherwise?
No if we just consider the snippet posted. Yes in the broader context. In the answer by Alexandre C. it is already addressed why returning a pointer-to-non-const-member in a const-qualified member function does not violate the const-correctness.
Of course, it is 2023 now and if anyone has a modern compiler that supports c++11 they should just use explicit operator bool()
and forget all these. However, there may still be poor guys who need to work with legacy environment restricted to c++03, and in that case maybe the answer by Alexandre C. provides a possibly better alternative. After all, the original version of safe bool idiom does not compile with new compilers even if the standard is set to c++03.
const
is not necessary. A body for the function is not either, since you're not supposed to call the function anyway. – Dwyersafe_bool
template I have deep in my VS2010 current project and it has a body. It hasconst
too. – Dwyer