If you're in the office and run out of paper, what do you do? Of course, you walk to your trusty photocopier and make several copies of a white sheet of paper.
As silly as it sounds, this is often what implicit conversions do: unnecessarily go through the trouble of creating temporaries, just to perform some trivial operation on them and toss them away (see Item 40). A common example is string comparison:
class String {
// … String( const char* text );
// enables implicit conversion }; bool operator==( const String&, const String& );
// … somewhere in the code … if( someString == "Hello" ) {... }
Having seen the definitions above, the compiler will compile the comparison as if you had written
s == String("Hello") . This can be quite wasteful, considering that you don't need to copy the characters just to read them. The solution to this problem is simple: define overloads that avoid the conversion. For example:
bool operator==( const String& lhs, const String& rhs );
// #1 bool operator==( const String& lhs, const char* rhs );
// #2 bool operator==( const char* lhs, const String& rhs );
// #3
That looks like a lot of code duplication, but in reality it is only "signature duplication" because all three typically use the same back-end function. You're unlikely to commit a premature optimization heresy (see Item 8) with such simple overloads, and it's
de bon goût to provide them especially when designing a library when it's difficult to predict in advance what common types will be in performance-sensitive code.