From @Xeo's excellent c++-faq
question: Is the safe-bool idiom obsolete in C++11? I learned that the safe bool idiom is no longer needed, because an explicit
user-defined conversion to bool
will be automatically invoked in contexts where the safe bool was needed in C++03.
However, the ability to overload operators such as &&
, ||
and !
seems to circumvent this.
Cases where operator!
is necessary beyond provision of conversion to bool
are rare, as are operator&&
and operator||
, but C++ expression tree implementations (used for deferred execution and symbolic math techniques) do need to override these.
Does "contextual conversion" take place when a user-defined operator is being invoked? What sort of SFINAE incantation is needed to make sure that a definition of operator&&
or operator||
will work correctly both with types implementing "safe bool" and those designed for "contextual conversion"?
To clarify, given:
class uses_safe_bool
{
void f() {};
typedef void (uses_safe_bool::* safe_bool)();
public:
operator safe_bool() const { return (rand() & 1)? &uses_safe_bool::f: 0; }
};
class uses_explicit_bool
{
public:
explicit operator bool() const { return rand() & 1; }
};
template<typename T>
class deferred_expression
{
// Not convertible to bool
public:
T evaluate() const;
};
What signatures are required for operator||
such that the following expressions are all valid:
deferred_expression<bool> db;
uses_safe_bool sb;
uses_explicit_bool eb;
int i;
auto test1 = sb || db;
auto test2 = eb || db;
auto test3 = true || db;
auto test4 = false || db;
auto test5 = i || db;
these use a different overload:
auto test6 = db || db;
deferred_expression<int> di;
auto test7 = di || db;
and the following are rejected at compile-time:
std::string s;
auto test7 = s || db;
std::vector<int> v;
auto test8 = v || db;
deferred_expression<std::string> ds;
auto test9 = ds || db;