09 - user defined convertions
It's possible to define custom convertions by overloading operator T
. The operator must be a member function that takes 0 arguments.
By default, the convertion is implicit. Since C++11 explicit
can be used to disallow implicit convertions - using it is strongly recommended.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// all code inside fraction class explicit operator float() const { return static_cast<float>(numerator()) / denominator(); } explicit operator double() const { return static_cast<double>(numerator()) / denominator(); } explicit operator long double() const { return static_cast<long double>(numerator()) / denominator(); } explicit operator bool() const { return numerator() != 0; } // not needed in C++11 and later bool operator!() const { return !operator bool(); } |
operator!
must be a member function that takes 0 arguments. It has no restrictions on the return type although there is very little point in a type different than bool
. Since C++11 !
performs contextual convertion to bool
so overloading operator bool
is enough - expressions like if (!obj)
where !
is not overloaded will be treated as if (!static_cast<bool>(obj))
.
Example usage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void fd(double); void fb(bool); fraction fr(1, 2); double d1 = fr; // error if "explicit" is used double d2 = static_cast<double>(fr); // ok fd(fr); // error if "explicit" is used fd(static_cast<double>(fr)); // ok bool is_non_zero1 = fr; // error if "explicit" is used bool is_non_zero2 = static_cast<bool>(fr); // ok fb(fr); // error if "explicit" is used fb(static_cast<bool>(fr)); // ok // always ok, "if" acts as an explicit convertion to bool if (fr) {} // C++ <= 98: ok even if only operator bool is overloaded (convertion is implicit) // C++ >= 11: ok even if only explicit operator bool is overloaded (! applies contextual convertion) if (!fr) {} |
Trivia
The explicit
convertion feature has been added after it has been realized that user-defined implicit convertions are hardly ever useful. Expressions like if (a == b)
where a
and b
are objects of different types would compile - the compiler would implicitly convert both objects to bool
and then compare them.
To solve this (in era before C++11), there was a safe bool idiom. A class did not overload convertion to bool
, but to a member function pointer or a member data pointer, which themselves are convertible to bool
. Then it added some template overloads of operator==
and operator!=
which call empty member function to cause compilation errors on specific (unwanted) comparisons. In short, it was a pretty complex idiom to implement correctly, only to prevent certain unwanted implicit convertions to bool
.
After C++11 introduced explicit
for user-defined convertions, the safe bool idiom is now obsolete.
Recommendation
Make user-defined convertions explicit
, especially convertions to bool
.