heydude
heydude

Reputation: 23

'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?

Upvotes: 2

Views: 544

Answers (2)

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385264

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.

Upvotes: 4

ildjarn
ildjarn

Reputation: 62985

Don't restrict your operator<< only to working with wstringstream, write it so it will work with any wide stream:

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

or with any stream (wide or narrow):

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

Upvotes: 9

Related Questions