Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
334 views
in Technique[技术] by (71.8m points)

c++11 - Are implicit conversions good or bad in modern C++?

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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 to int. 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 type int. 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;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...