10.13 explicit Constructors and Conversion Operators

Recall that we’ve been declaring as explicit every constructor that can be called with one argument, including multiparameter constructors for which we specify default arguments. With the exception of copy constructors, any constructor that can be called with a single argument and is not declared explicit can be used by the compiler to perform an implicit conversion. The constructor’s argument is converted to an object of the class in which the constructor is defined. The conversion is automatic—a cast is not required.

In some situations, implicit conversions are undesirable or error-prone. For example, our Array class in Fig. 10.10 defines a constructor that takes a single int argument. The intent of this constructor is to create an Array object containing the number of elements specified by the int argument. However, if this constructor were not declared explicit it could be misused by the compiler to perform an implicit conversion.

Common Programming Error 10.5

Unfortunately, the compiler might use implicit conversions in cases that you do not expect, resulting in ambiguous expressions that generate compilation errors or result in execution-time logic errors.

Accidentally Using a Single-Argument Constructor as a Conversion Constructor

The program (Fig. 10.12) uses the Array class of Figs. 10.1010.11 to demonstrate an improper implicit conversion. To allow this implicit conversion, we removed the explicit keyword from line 13 in Array.h (Fig. 10.10).

Fig. 10.12 Single-argument constructors and implicit conversions.

Alternate View

 1   // Fig. 10.12: fig10_12.cpp
 2   // Single-argument constructors and implicit conversions.
 3   #include <iostream>
 4   #include "Array.h"
 5   using namespace std;
 6
 7   void outputArray(const Array&); // prototype
 8
 9   int main() {
10      Array integers1{7}; // 7-element Array
11      outputArray(integers1); // output Array integers1
12      outputArray(3); // convert 3 to an Array and output Array’s contents
13   }
14
15   // print Array contents
16   void outputArray(const Array& arrayToOutput) {
17      cout << "The Array received has " << arrayToOutput.getSize()
18         << " elements. The contents are:
" << arrayToOutput << endl;
19   }

The Array received has 7 elements.
The contents are: 0 0 0 0 0 0 0

The Array received has 3 elements.
The contents are: 0 0 0

Line 10 in main (Fig. 10.12) instantiates Array object integers1 and calls the single-argument constructor with the int value 7 to specify the number of elements in the Array. Recall from Fig. 10.11 that the Array constructor that receives an int argument initializes all the Array elements to 0. Line 11 in Fig. 10.12 calls function outputArray (defined in lines 16–19), which receives as its argument a const Array& to an Array. The function outputs the number of elements in its Array argument and the contents of the Array. In this case, the size of the Array is 7, so seven 0s are output.

Line 12 calls function outputArray with the int value 3 as an argument. However, this program does not contain a function called outputArray that takes an int argument. So, the compiler determines whether the argument 3 can be converted to an Array object. Because class Array provides a constructor with one int argument and that constructor is not declared explicit, the compiler assumes the constructor is a conversion constructor and uses it to convert the argument 3 into a temporary Array object containing three elements. Then, the compiler passes the temporary Array object to function outputArray to output the Array’s contents. Thus, even though we do not explicitly provide an outputArray function that receives an int argument, the compiler is able to compile line 12. The output shows the contents of the three-element Array containing 0s.

Preventing Implicit Conversions with Single-Argument Constructors

The reason we’ve been declaring every single-argument contructor preceded by the keyword explicit is to suppress implicit conversions via conversion constructors when such conversions should not be allowed. A constructor that’s declared explicit cannot be used in an implicit conversion. In the example of Figure 10.13, we use the original version of Array.h from Fig. 10.10, which included the keyword explicit in the declaration of the single-argument constructor in line 13


explicit Array(int = 10); // default constructor

Figure 10.13 presents a slightly modified version of the program in Fig. 10.12. When this program in Fig. 10.13 is compiled, the compiler produces an error message, such as


'void outputArray(const Array &)': cannot convert argument 1
from 'int' to 'const Array &'

on Visual C++, indicating that the integer value passed to outputArray in line 12 cannot be converted to a const Array&. Line 13 demonstrates how the explicit constructor can be used to create a temporary Array of 3 elements and pass it to function outputArray.

Error-Prevention Tip 10.4

Always use the explicit keyword on single-argument constructors unless they’re intended to be used as conversion constructors.

Fig. 10.13 Demonstrating an explicit constructor.

Alternate View

 1   // Fig. 10.13: fig10_13.cpp
 2   // Demonstrating an explicit constructor.
 3   #include <iostream>
 4   #include "Array.h"
 5   using namespace std;
 6
 7   void outputArray(const Array&); // prototype
 8
 9   int main() {
10      Array integers1{7}; // 7-element Array
11      outputArray(integers1); // output Array integers1
12      outputArray(3); // convert 3 to an Array and output Array’s contents
13      outputArray(Array{3}); // explicit single-argument constructor call
14   }
15
16   // print Array contents
17   void outputArray(const Array& arrayToOutput) {
18      cout << "The Array received has " << arrayToOutput.getSize()
19         << " elements. The contents are:
" << arrayToOutput << endl;
20   }

C++11: explicit Conversion Operators

Just as you can declare single-argument constructors explicit, you can declare conversion operators explicit to prevent the compiler from using them to perform implicit conversions. For example, the prototype


explicit MyClass::operator string() const;

declares MyClass’s string cast operator explicit, thus requiring you to invoke it explicitly with static_cast.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset