17

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.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • Certainly any kind of ownership transfer must never be implicit. (cough `auto_ptr` cough) – Kerrek SB Mar 04 '14 at 00:28
  • 1
    Implicit conversions can cause tons of trouble, and I don't think the 'overhead' of 6 characters for writing `.get()` is worth it. – Xeo Mar 04 '14 at 00:28
  • @Xeo: So is the proposal of `scoped_resource` with the implicit conversion wrong? – Mr.C64 Mar 04 '14 at 00:36
  • @KerrekSB: We're not talking about ownership transfer, but just "observing" the wrapped "raw" thing. – Mr.C64 Mar 04 '14 at 00:37
  • I found 4 votes to close this question: this seems just ridicolous to me. Those who vote for closing this question should clarify the reasons why (assuming there are any _valid_). This question seems to me elaborated and well crafted. And if there is something that can be made to make it better, that can be inserted into comments. – Mr.C64 Mar 04 '14 at 00:40
  • 1
    @Mr.C64: Yeah, but that's also tricky. Suppose you have some smart pointer class `P` so that `P p(new T)` works. Now if `p` can be implicitly converted to `T*`, will `P q(p)` work? And `delete p`? So you have to be really careful even when you're just *near* ownership issues. Generally, passing by reference solves this because you use the familiar and concise `*p` syntax. – Kerrek SB Mar 04 '14 at 00:43
  • @KerrekSB: So is there a bug in `scoped_resource` proposal due to the _implicit_ raw handle conversion? (I noted that both STL strings and STL smart pointers have the **explicit** _"getter"_, be it `c_str()` or `get()`, instead that proposed RAII wrapper was different from this point of view.) – Mr.C64 Mar 04 '14 at 00:46
  • 2
    I think implicit conversions are good/convenient for the application programmer, but bad for library developers as they introduce complexity and other issues: A good example is the interplay between implicit conversion and template deduction. From a library perspective, it is easier to constrain language features. For that, you could even say that library is easier to maintain if you remove some of the overloading and default parameters. It's less convenient for application programmers, but there is less ambiguity and complexity. So, it's really a choice between convenience and simplicity. – thor Mar 04 '14 at 00:48
  • @TingL: Thanks. I'd be happy to upvote your comment if it were posted in the answers section. – Mr.C64 Mar 04 '14 at 00:51
  • @Mr.C64: Don't know, I haven't read the proposal. Did it make it through the recent meeting? – Kerrek SB Mar 04 '14 at 00:54
  • @Mr.C64 I was trying to write an answer when the question was blocked minutes after being posted. – thor Mar 04 '14 at 07:20

3 Answers3

3

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;
}
Victor
  • 13,914
  • 19
  • 78
  • 147
1

I think implicit conversions are good/convenient for the application programmer, but bad for library developers as they introduce complexity and other issues: A good example is the interplay between implicit conversion and template deduction.

From a library perspective, it is easier to constrain language features to a minimal set. For that, you could even say that library is easier to maintain if you remove some of the overloading and default parameters. It's less convenient for application programmers, but there is less ambiguity and complexity.

So, it's really a choice between convenience and simplicity.

thor
  • 21,418
  • 31
  • 87
  • 173
0

It highly depends on exact conversion type, but, in general, there are some areas where implicit conversions may introduce problems, whereas explicit rather may not.

Generally, doing anything explicitly in programming is a more safe way.

There is a good source of information on C++ in general and on implicit and explicit conversions in particular.

dnl-blkv
  • 2,037
  • 1
  • 17
  • 22