4

我正在将 MFC 程序从 MBCS 转换为 Unicode。我发现插入运算符 << 与 CStringA 和 CStringW 实例的工作方式不同。

// char
std::ostringstream c_oss;
CStringA c_s("Hello");
c_oss << c_s;
TRACE("%s\n", c_oss.str().c_str());

// wchar_t
std::wostringstream w_oss;
CStringW w_s(L"World");
w_oss << w_s;
TRACE(L"%s\n", w_oss.str().c_str());

我希望这会打印“Hello\nWorld\n”,但它会打印“Hello\n14,5E6,B38\n”。也就是说,它打印的是 w_s 数据的地址而不是数据。

如果我调试到 w_oss << w_s,我可以看到插入 const void* 的重载被选中,而不是插入 const wchar_t* 的重载。它适用于 char 版本。如果我明确应用大小写 (LPCTSTR) 或 (const wchar_t*),它适用于 wchar_t 版本。

任何想法为什么 wchar_t 版本与 char 版本的工作方式不同?

4

3 回答 3

3

的宽字符版本operator<<是一个模板,因此需要精确的参数匹配。没有用户定义的转换,例如CStringW::operator wchar_t*()隐式执行。

OTOHvoid*相同运算符的版本不是模板,并且愉快地使用用户定义的转换运算符。

于 2012-10-15T17:50:03.147 回答
2

考虑到 VS2010 SP1,我发现在<ostream>标头中存在std::ostringstreamand的重载const char*

template<class _Traits> inline
    basic_ostream<char, _Traits>& operator<<(
        basic_ostream<char, _Traits>& _Ostr,
        const char *_Val)
    {    // insert NTBS into char stream
         ...

std::wostringstream但是和似乎没有类似的重载const wchar_t*

如果将其添加到源代码中,发送CStringWwithoperator<<似乎可以工作(我个人的偏好:使用CString::GetString()字符串流的方法和operator<<):

namespace std {

template<class _Traits> inline
    basic_ostream<wchar_t, _Traits>& operator<<(
    basic_ostream<wchar_t, _Traits>& _Ostr,
    const wchar_t *_Val)
{
    ATLTRACE("It's me, the new overload!\n");

    typedef wchar_t _Elem;

    //
    // *** Copy and paste *** the source code from the following overload:
    //
    // template<class _Elem,
    //  class _Traits> inline
    //    basic_ostream<_Elem, _Traits>& operator<<(
    //    basic_ostream<_Elem, _Traits>& _Ostr, const _Elem *_Val)
    //

    //
    // NOTE: I don't want to infringe any copyright.
    //
    // Moderators please delete the following lines if they
    // infringe any copyright.
    //

    typedef basic_ostream<_Elem, _Traits> _Myos;

    ios_base::iostate _State = ios_base::goodbit;
    streamsize _Count = (streamsize)_Traits::length(_Val);  // may overflow
    streamsize _Pad = _Ostr.width() <= 0 || _Ostr.width() <= _Count
        ? 0 : _Ostr.width() - _Count;
    const typename _Myos::sentry _Ok(_Ostr);

    if (!_Ok)
        _State |= ios_base::badbit;
    else
    {   // state okay, insert
        _TRY_IO_BEGIN
            if ((_Ostr.flags() & ios_base::adjustfield) != ios_base::left)
                for (; 0 < _Pad; --_Pad)    // pad on left
                    if (_Traits::eq_int_type(_Traits::eof(),
                        _Ostr.rdbuf()->sputc(_Ostr.fill())))
                    {   // insertion failed, quit
                        _State |= ios_base::badbit;
                        break;
                    }

                    if (_State == ios_base::goodbit
                        && _Ostr.rdbuf()->sputn(_Val, _Count) != _Count)
                        _State |= ios_base::badbit;

                    if (_State == ios_base::goodbit)
                        for (; 0 < _Pad; --_Pad)    // pad on right
                            if (_Traits::eq_int_type(_Traits::eof(),
                                _Ostr.rdbuf()->sputc(_Ostr.fill())))
                            {   // insertion failed, quit
                                _State |= ios_base::badbit;
                                break;
                            }
                            _Ostr.width(0);
                            _CATCH_IO_(_Ostr)
    }

    _Ostr.setstate(_State);
    return (_Ostr);
}

} // namespace std
于 2012-10-15T18:08:23.037 回答
2

我想,nm的答案是正确的。官方的解释相当模糊,但含义相同(MSDN about IO with std::wcout):

没有强制转换, cs 被视为 void* 并且 wcout 打印对象的地址。这种行为是由模板参数推导和重载解析之间的微妙交互引起的,它们本身是正确的并且符合 C++ 标准。

于 2012-10-15T18:10:08.017 回答