In this proposal:
N3830 Scoped Resource - Generic RAII Wrapper for the Standard Library
a scoped_resource
RAII wrapper is presented.
On page 4, there is some code like this:
auto hFile = std::make_scoped_resource( ... ); ... // cast operator makes it seamless to use with other APIs needing a HANDLE ReadFile(hFile, ...);
The Win32 API ReadFile()
takes a raw HANDLE
parameter, instead hFile
is an instance of scoped_resource
, so to make the above code work, there is an implicit cast operator implemented by scoped_resource
.
But, wasn't the "modern" recommendation to avoid these kind of implicit conversions?
For example, ATL/MFC CString
has an implicit conversion (cast operator) to LPCTSTR
(const char/wchar_t*
, i.e. a raw C-string pointer), instead STL strings have an explicit c_str()
method.
Similarly, smart pointers like unique_ptr
have an explicit get()
method to access the underlying wrapped pointer; and that recommendation against implicit conversion seems also present in this blog post:
Reader Q&A: Why don’t modern smart pointers implicitly convert to *?
So, are these implicit conversions (like ATL/MFC CString
and the newly proposed scoped_resource
) good or not for modern C++?
From a coding perspective, I'd say that being able to simply directly pass a RAII wrapper - be it CString
or scoped_resource
- to a C API expecting a "raw" parameter (like a raw C string pointer, or raw handle), relying on implicit conversions, and without calling some .GetString()
/.get()
method, seems very convenient.
Everyone knows Implicit Conversion is bad. It can ruin SARGability, defeat index usage, and burn up your CPU like it needs some Valtrex.
Implicit Type Conversion is also known as 'automatic type conversion'. It is done by the compiler on its own, without any external trigger from the user. It generally takes place when in an expression more than one data type is present.
Advantages of Type Conversion: This is done to take advantage of certain features of type hierarchies or type representations. It helps to compute expressions containing variables of different data types.
An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.
Below is quote from C++ Primer 5th edition.
Caution: Avoid Overuse of Conversion Functions
As with using overloaded operators, judicious use of conversion operators can greatly simplify the job of a class designer and make using a class easier. However, some conversions can be misleading. Conversion operators are misleading when there is no obvious single mapping between the class type and the conversion type.
For example, consider a class that represents a Date. We might think it would be a good idea to provide a conversion from
Date
toint
. However, what value should the conversion function return? The function might return a decimal representation of the year, month, and day. For example, July 30, 1989 might be represented as the int value 19800730. Alternatively, the conversion operator might return an int representing the number of days that have elapsed since some epoch point, such as January 1, 1970. Both these conversions have the desirable property that later dates correspond to larger integers, and so either might be useful.The problem is that there is no single one-to-one mapping between an object of type
Date
and a value of typeint
. In such cases, it is better not to define the conversion operator. Instead, the class ought to define one or more ordinary members to extract the information in these various forms.
So, what I can say is that in practice, classes should rarely provide conversion operators. Too often users are more likely to be surprised if a conversion happens automatically than to be helped by the
existence of the conversion. However, there is one important exception to this rule of
thumb: It is not uncommon for classes to define conversions to bool
.
Under earlier versions of the standard, classes that wanted to define a conversion to
bool faced a problem: Because bool is an arithmetic type, a class-type object that is
converted to bool can be used in any context where an arithmetic type is expected.
Such conversions can happen in surprising ways. In particular, if istream
had a
conversion to bool
, the following code would compile:
int i = 42;
cin << i; // this code would be legal if the conversion to bool were not explicit!
You should use explicit
conversion operators. Below is a little example:
class small_int
{
private:
int val;
public:
// constructors and other members
explicit operator int() const { return this->val; }
}
... and in the program:
int main()
{
SmallInt si = 3; // ok: the SmallInt constructor is not explicit
si + 3; // error: implicit is conversion required, but operator int is explicit
static_cast<int>(si) + 3; // ok: explicitly request the conversion
return 0;
}
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