Professional C__ - Marc Gregoire [325]
The usual pre-C++11 solution to this conundrum is to make the constructor in question explicit, so that the automatic conversion using that constructor is prevented. However, we don’t want that constructor to be explicit because we generally like the automatic conversion of doubles to SpreadsheetCells, as explained in Chapter 7. You might try to solve the problem by making the double conversion operator explicit, but this is not allowed in pre-C++11. With C++11, this is allowed, as follows:
explicit operator double() const;
Code snippet from Conversions\SpreadsheetCell\SpreadsheetCell.h
The following code demonstrates its use:
SpreadsheetCell cell = 6.6; // [1]
string str = cell; // [2]
double d1 = static_cast double d2 = static_cast Code snippet from Conversions\SpreadsheetCell\SpreadsheetCellTest.cpp The different lines in the preceding code are explained in the following list: [1] Uses the implicit conversion from a double to a SpreadsheetCell. Because this is in the declaration, this is done by calling the constructor that accepts a double. [2] Uses the operator string() conversion operator. [3] Uses the operator double() conversion operator. Note that because this conversion operator is now declared explicit, the cast is required. [4] Uses the implicit conversion of 3.3 to a SpreadsheetCell, followed by operator+ on two SpreadsheetCells, followed by a required explicit cast to invoke operator double(). Conversions for Boolean Expressions Sometimes it is useful to be able to use objects in Boolean expressions. For example, programmers often use pointers in conditional statements like this: if (ptr != nullptr) { /* Perform some dereferencing action. */ } Sometimes they write shorthand conditions such as: if (ptr) { /* Perform some dereferencing action. */ } Other times, you see code as follows: if (!ptr) { /* Do something. */ } Currently, none of the preceding expressions compile with the Pointer smart pointer class defined earlier. However, you can add a conversion operator to the class to convert it to a pointer type. Then, the comparisons to nullptr, as well as the object alone in an if statement, will trigger the conversion to the pointer type. The usual pointer type for the conversion operator is void*. Here is the modified Pointer class: template class Pointer { public: // Omitted for brevity operator void*() const { return mPtr; } // Omitted for brevity }; Code snippet from Conversions\Pointer\Pointer.h Now the following code compiles and does what you expect: void process(Pointer { if (p != nullptr) { cout << "not nullptr" << endl; } if (p != NULL) { cout << "not NULL" << endl; } if (p) { cout << "not nullptr" << endl; } if (!p) { cout << "nullptr" << endl; } } int main() { Pointer process(smartCell); cout << endl; Pointer process(anotherSmartCell); } Code snippet from Conversions\Pointer\PointerTest.cpp The output is as follows: nullptr not nullptr not NULL not nullptr Another alternative is to overload operator bool() instead of operator void*(). After all, you’re using the object in a Boolean expression; why not convert it directly to a bool? You could write your Pointer class as follows: template class Pointer { public: // Omitted for brevity operator bool() const { return mPtr != nullptr; } // Omitted for brevity }; Code snippet from Conversions\Pointer\PointerBool.h Comparing with NULL as shown in the following code still works but might generate a compiler warning because the compiler will convert NULL to a non-pointer type int, 0: if (p != NULL) { cout << "not NULL" << endl; } The following comparison tests also still work as before: if (p) { cout << "not nullptr" << endl; } if (!p) { cout << "nullptr" << endl; } However, with operator