使用 iostream 打印 CComBSTR (std::wcout)

Printing CComBSTR with iostream (std::wcout)

以下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;
}

打印

033FA16C
test

我尝试使用调试器调查在每种情况下发生了哪些转换,但两次都进入了 operator BSTR。那么为什么第一行打印地址而第二行打印文本呢?

我们可以从中完全删除 ATL,因为这实际上是 wcout 如何工作的问题。

考虑以下最小示例:

#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

(live demo)

在您的示例中,从 CComBSTRBSTRthe implicit conversion 被触发,但 不是 由实例化 operator<<(const wchar_t*)(因为转换为"user-defined",模板参数匹配不考虑用户自定义转换)。唯一可行的候选者是非模板 operator<<(const void*),您转换后的 BSTR 将传递给该模板。

实际上在标准 (LWG 2342) 中有一项针对 "fix" 的提案,提案的文本对此进行了更详细的解释。

总结:

For wide streams argument types wchar_t const* and wchar_t are supported only as template parameters. User defined conversions are not considered for template parameter matching. Hence inappropriate overloads of operator<< are selected when an implicit conversion is required for the argument, which is inconsistent with the behavior for char const* and char, is unexpected, and is a useless result.

唯一可行的重载是采用 const void* 的重载,因为每个指针都可以隐式转换为 const void*,这就是您得到的结果。