I have seen comments or answers on SoF stating that overloading the cast operator to bool is dangerous and I should prefer the void* operator
instead. Still I would like to ask if it is dangerous practice to use this operator in my use case and if yes why.
I implement a simply geometry library and one of its most basic classes is a point that I define in the following way:
struct point {
double x, y;
bool real;
point(double x_, double y_): x(x_), y(y_), real(true) {}
point(): x(0), y(0), real(true) {}
operator bool() {
return real;
}
};
I will try to explain why I need the cast operator to bool. I have another class called line and I have a function declared as follows:
point intersect(const line& a, const line& b);
Now having overloaded the cast operator to bool of a point I can write both: point p = intersect(a,b);
and if (intersect(a,b))
thus either getting the intersection point of two lines or checking if two lines intersect. I use this in other places as well but I believe this example is enough to show the usage of the cast operator. Is my usage of the cast operator dangerous and if it is could you please give an example when the effect will not be as expected?
EDIT: one small addition thanks to juanchopanza : in this project I am limited to not using c++11, so I can not make the operator explicit.
That is not good enough. Since you're stuck with C++03, safe-bool idiom is what you should be using if you need such a thing at all (otherwise, explicit
conversion function should be preferred).
An alternative solution is to return boost:optional
instead:
boost::optional<point> intersect(const line& a, const line& b);
I would adopt this approach (even in C++11), as it looks semantically better — parallel lines don't have intersection point, so the return value should indeed be optional (or maybe value).
Yes, this is dangerous. Just for example, let's consider your point
exactly as-is, with no definition of operator+
. Despite that lack, if we try to add two point
objects, the compiler won't object at all:
point a, b;
std::cout << a + b;
This works by converting each point
to bool
, then adding the bool
s. In an integer context, false
converts to 0 and true
converts to 1, so we can expect the code above to print out 2. Of course, I've just used addition as an example. You could just as well do subtraction, multiplication, division, bitwise operations, logical operations, etc. All of them will compile and execute, but obviously produce worthless results. Likewise, if we pass a point
to some function that only accepts a numeric type, the compiler won't stop s or complain -- it'll just convert to bool
, and the function will get either 0
or 1
depending on whether real
was true or not.
The safe-bool idiom is safe (well, less dangerous anyway) because you can test a void *
in a Boolean context, but a void *
won't implicitly convert to much of anything else like bool
will. You (mostly1) can't accidentally do arithmetic, bitwise operations, etc., on them.
1. There are a few holes anyway, mostly involving calling something else that does some sort of explicit conversion on the void *
. Being able to mark conversion operators explicit
is better, but honestly they give a lot more improvement in readability than safety.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With