проблема порядка аргументов 'stringstream' - PullRequest
2 голосов
/ 02 июня 2011

У меня странная проблема с stringstream.

#include "stdafx.h"
#include "iostream"
#include "sstream"

using namespace std;

struct Test
{
    float f;
};

wstringstream& operator <<( wstringstream& sstream , const Test& test )
{
    sstream << test.f;
    return sstream;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Test a;
    a.f = 1.2f;

    wstringstream ss;
    ss << L"text" << a << endl; // error C2679!
    ss << a << L"text" << endl; // it works well..

    getchar();
    return 0;
}

Проблема здесь:

ss << L"text" << a << endl; // error C2679!
ss << a << L"text" << endl; // it works well..

Единственная разница между этими двумя утверждениями - это порядок аргументов.Почему первое утверждение терпит неудачу, а второе работает?

Ответы [ 2 ]

9 голосов
/ 02 июня 2011

Не ограничивайте ваш operator<< только работой с wstringstream, напишите его так, чтобы он работал с любым широким потоком:

std::wostream& operator <<(std::wostream& sstream, Test const& test)
{
    return sstream << test.f;
}

или с любым потоком (широким или узким):

template<typename CharT, typename TraitsT>
std::basic_ostream<CharT, TraitsT>&
operator <<(std::basic_ostream<CharT, TraitsT>& sstream, Test const& test)
{
    return sstream << test.f;
}
4 голосов
/ 02 июня 2011

Краткий ответ

Проблема в том, что ss << L"text" дает вам std::wostream, а не std::wstringstream.

Вы только создали operator<< для std::wstringstream, поэтому следующая операция (которую вы пытаетесь выполнить на a) не удалась.

Длинный ответ

Когда вы пишете что-то вроде

ss << L"text" << a << endl;

вы не вызываете функцию с четырьмя аргументами.

Фактически вы объединяете несколько операций:

((ss << L"text") << a) << endl;

Это работает, потому что каждая операция operator<< возвращает ссылку на исходный объект потока, так что вы можете продолжить цепочку следующих операций таким образом.

Но поскольку iostreams формируют иерархию наследования и поскольку operator<< применим к любому выходному потоку, тип возврата из вашей операции на wstringstream является чем-то немного менее конкретным, чем wstringstream.

Фактически, ss << L"text" оценивается как wostream& (wostream является одним из базовых классов wstringstream). Ссылка все еще ссылается на тот же оригинальный объект потока ... но у него есть тип базового класса.

Итак, ваша вторая операция с a имеет следующие активные операнды:

  • a wostream& (на LHS)
  • a Test (по RHS)

Но у вас нет wostream& operator<<(wostream&, Test const&). Вы только создали wstringstream& operator<<(wstringstream& sstream, Test const& test), поэтому совпадений нет.

Таким образом, фактически, при создании operator<< для широких iostreams, вы должны заставить его работать на все wostream с (ясно, что нет никаких оснований ограничивать его wstringstreams):

wostream& operator<<(wostream& sstream, Test const& test)
{
    sstream << test.f;
    return sstream;
}

Идем дальше, зачем ограничиваться широкими потоками? Почему не нормальные тоже?

template<typename CharT, typename TraitsT>
std::basic_ostream<CharT, TraitsT>&
operator<<(std::basic_ostream<CharT, TraitsT>& sstream, Test const& test)
{
    sstream << test.f;
    return sstream;
}

Теперь вы сможете правильно передавать потоки объектов вашего Test класса в wostream s, ostream s и всех их потомков.

...