3

Today I experienced an interesting behaviour with user-defined implicit conversion operators.

Let's take this piece of code:

struct Widget {
    Widget(uint64_t) {

    }

    Widget(const std::string &) {

    }

    operator uint64_t() {
        return static_cast<uint64_t>(123456789UL);
    }

    operator std::string() {
        return std::string("Hello");
    }
};

A basic struct which can implicitly be converted to either a uint64_t or a std::string.

Now, try to print out a Widget instance through std::cout :

#include <iostream>

int main() {
    using std::cout;
    Widget w(123456);

    cout << w;
}

For whatever reason, the Widget will always be converted to a uint64_t. At first I would expect the call to be ambiguous, and be qualifed with a standard explicit conversion to compile :

int main() {
    using std::cout;
    Widget w(123456);

    cout << static_cast<uint64_t>(w);

}

But for a reason I'm ignoring right now, operator uint64_t is selected. I tried to look at the C++ specification, but couldn't find anything useful to answer my question.

Can anyone help me figuring out what is the compiler doing with overload resolution ?

octal
  • 363
  • 3
  • 8

2 Answers2

4

uint64_t conversion is preferred. The reason is simply that << is overloaded as template for strings (basic_strings). Compilers will always prefer exact match over templates on overload resolution.

stardust
  • 5,918
  • 1
  • 18
  • 20
  • Thanks, I didn't realize that operator<< was overloaded as a template for std::basic_string. It explains it all. If I replace std::string by double it is now ambiguous – octal Apr 18 '13 at 16:54
  • @octal http://en.cppreference.com/w/cpp/string/basic_string/operator_ltltgtgt – stardust Apr 18 '13 at 16:58
4

Of course you can find it in C++ specification.

Read Overload Resolution in §13.3.1/2 and §13.3.1/3

Overload resolution selects the function to call in seven distinct contexts within the language...

 

  • First, a subset of the candidate functions (those that have the proper number of arguments and meet certain other conditions) is selected to form a set of viable functions (13.3.2).
  • Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.

 

In brief, compiler first lists candidates to use, then tries to find best fit and more efficient overload.

masoud
  • 55,379
  • 16
  • 141
  • 208