8

Consider this code:

#include <iostream>
using namespace std;

class X {
public:
    operator const wchar_t* () const { return L"Hello"; }
};

void f(const void *) {
    wcout << L"f(const void*)\n";
}

void f(const wchar_t*) {
    wcout << L"f(const wchar_t*)\n";
}

int main() {
    X x;
    f(x);

    wcout << x;
}

The output is (compiled with the VS2015 C++ compiler):

f(const wchar_t*)
00118B30

So it seems that the compiler selects the expected const wchar_t* overload for f (as there's an implicit conversion from X to const wchar_t*).

However, it seems that wcout << x picks the const void* overload, instead of the const wchar_t* one (printing an address, instead of a wchar_t string).

Why is this?

P.S. I know that the proper way of printing X is to implement an overload of operator<< like wostream& operator<<(wostream& , const X&), but that is not the point of the question.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • 1
    This might not be it, but your program has [undefined behavior](https://stackoverflow.com/questions/8947949/mixing-cout-and-wcout-in-same-program). You can't mix and match `std::cout` and `std::wcout`. – Rakete1111 Jul 28 '17 at 16:49
  • @Rakete1111: That's not the point. Feel free to use wcout instead of cout to track the selected overloads of f :) – Mr.C64 Jul 28 '17 at 16:51
  • 2
    It might not be the point but asking why a program with undefined behavior behaves a certain way doesn't make any sense, even if in your case it doesn't change anything. – Rakete1111 Jul 28 '17 at 16:52
  • @KingThrushbeard: No, printing an object does not yield its address, unless you write a function which does that. – Benjamin Lindley Jul 28 '17 at 16:55
  • 2
    For those complaining about mixing wcout and cout, I changed the code to use wcout instead of cout in the trace messages inside f overloads. Same output. – Mr.C64 Jul 28 '17 at 16:55
  • 2
    Note that the operator which prints `const void*` is a member function, and the one which prints `const wchar_t*` is a non-member function. I'm not familiar enough with the lookup rules to cite why that should matter, but I'm pretty sure it does. – Benjamin Lindley Jul 28 '17 at 16:58

1 Answers1

4

Because when deducing template arguments the conversion functions are not considered:

// Non-template member function.
basic_ostream& basic_ostream::operator<<( const void* value );

// Template non-member function.
template< class CharT, class Traits >
basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os, 
                                         const CharT* s );

The second declaration does not consider the conversion operator const wchar_t* () const.

I cannot find the standard quote, cppreference Template argument deduction, Implicit conversions says:

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 3
    If you remove the non-template it will not even compile, because implicit conversion doesn't happen for template argument deduction. :) – Rakete1111 Jul 28 '17 at 17:10