The following code:
#include <iostream>
#include <atlstr.h>
int main()
{
CComBSTR bstr(L"test");
std::wcout << bstr << std::endl;
std::wcout << static_cast<BSTR>(bstr) << std::endl;
}
prints
033FA16C
test
I tried to investigate which conversions take place in each case with a debugger but both time had stepped into operator BSTR
. So why the first line prints address while the second prints a text?
We can remove ATL from this entirely, as it's really a question of how wcout
works.
Consider the following minimal example:
#include <iostream>
struct Foo
{
operator const wchar_t*() const { return L"what"; };
};
int main()
{
Foo f;
std::wcout << f << std::endl;
std::wcout << (const wchar_t*)f << std::endl;
}
// Output:
// 0x400934
// what
In your example, the implicit conversion from CComBSTR
to BSTR
is triggered, but not by the template that instantiates operator<<(const wchar_t*)
(because the conversion is "user-defined", and user-defined conversions are not considered for template parameter matching). The only viable candidate then is the non-template operator<<(const void*)
, to which your converted BSTR
is passed.
There's actually a proposal to "fix" this in the standard (LWG 2342) and the text of the proposal explains this in more detail.
In summary:
For wide streams argument types
wchar_t const*
andwchar_t
are supported only as template parameters. User defined conversions are not considered for template parameter matching. Hence inappropriate overloads ofoperator<<
are selected when an implicit conversion is required for the argument, which is inconsistent with the behavior forchar const*
andchar
, is unexpected, and is a useless result.
The only remaining viable overload is the one taking const void*
and, since every pointer can implicitly convert to const void*
, that's what you're getting.