Implicit type conversions usually lead to more harm than good, so don’t provide conversion functions unless we’re sure we want them.
There are two kinds of functions allowing compilers to perform implicit type conversions:
- implicit type conversion operators
- non-explicit single-argument constructors
Implicit type conversion operators
|
|
This implicit type conversion function will be automatically invoked in contexts like this:
|
|
Suppose we forget to write an operator<<
for Rational
objects, and expect the attempt to print r
would fail due to the lack of appropriate operator<<
to call. However, in the lack of operator<<
, compilers are happy to find an acceptable sequence of implicit type conversions they could apply to make the call succeed. In this case, they will discover they could make the call succeed by implicitly converting r
to a double
by calling Rational::operator double
, leading to wrong (often unexpected) function being called.
Solution: replace the implicit type conversion operators with equivalent functions that don’t have the syntactically magic names, just like the explicit member function c_str
in string
type from standard C++ library (MECpp item 35):
|
|
|
|
Single-argument constructor
|
|
The problem of implicit type conversion occurs in following condition:
|
|
Here, we mistakenly omitted the subscripting syntax when typing a
. We expect this will cause compilers complaining, but they will not:
-
Compilers see a call to
operator==
taking typeArray<int>
(fora
) andint
(forb[i]
), and fail to find the corresponding function -
There’s a
operator==
taking twoArray<int>
type arguments, and compilers are able to convert theint
into anArray<int>
object by callingArray<int>
constructor that taking a singleint
as an argument, ending up with something like this1:1 2 3
for (int i = 0; i < 10; ++i) { if (a == static_cast< Array<int> >(b[i])) ... }
Each iteration through the loop compares the contents of a
with the contents of a temporary array of size b[i]
(whose contents are presumably undefined and will be created and destroyed in every loop, see MECpp item 19), which is both unwanted and tremendously inefficient behavior.
There are two solutions: use keyword explicit
or creating proxy classes.
Solution 1: Keyword explicit
|
|
Solution 2: Proxy classes
|
|
|
|
One of the rules governing implicit type conversions is that no sequence of conversions is allowed to contain more than one user-defined conversion (i.e., a call to a single-argument constructor or an implicit type conversion operator). The above class difinition adopting a general technique called proxy classes takes advantage of this rule, ending up with ideal behavior that the object constructions we want to allow are legal, but the implicit conversions we don’t want to allow are illegal (compilers in one implicit conversion can’t call two user-defined conversions, one from int
to ArraySize
and one from ArraySize
to Array<int>
).
Proxy objects can give us control over aspects of software’s behavior that is otherswise beyond our grasp. For more detail, refer to MECpp item 30.
-
The space separating the two “>” characters has its purpose: without it, the statement will be like
static_cast<Array<int>>(b[i])
, and some C++ compilers parsing “»” as a single token, ending up with a syntax error. ↩︎