Search code examples
c++visual-studiostringstreamvisual-studio-2008-sp1

'stringstream' argument order trouble


I'm experiencing a weird problem with 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;
}

The problem is here:

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

The only difference between these two statements is argument order. Why does the first statement fail whereas the second one works?


Solution

  • Short answer

    The problem is that ss << L"text" gives you a std::wostream, not a std::wstringstream.

    You only created an operator<< for std::wstringstream, so the next operation (which you're trying to do on a) fails.

    Long answer

    When you write something like

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

    you are not invoking a function with four arguments.

    You are, in fact, chaining multiple operations:

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

    This works because each operator<< operation returns a reference to the original stream object, so that you can continue chaining further operations on in this manner.

    But because iostreams form an inheritance hierarchy, and because operator<< is applicable to any output stream, the return type from your operation on wstringstream is something a little less specific than wstringstream.

    In fact, ss << L"text" evaluates to a wostream& (wostream being one of wstringstream's base classes). The reference still refers to the same, original stream object... but it has a base class type.

    So, your second operation involving a has the following active operands:

    • a wostream& (on the LHS)
    • a Test (on the RHS)

    But you have no wostream& operator<<(wostream&, Test const&). You only created a wstringstream& operator<<(wstringstream& sstream, Test const& test), so there's no match.

    So, in fact, when creating an operator<< for wide iostreams you should make it work for all wostreams (clearly there is no reason to limit it to wstringstreams):

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

    Going further, why limit yourself to wide streams? Why not normal ones too?

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

    Now you will be able to stream objects of your Test class into wostreams, ostreams, and all their descendants, properly.