0

I am trying to create a thin wrapper over an ostringstream instance which delegates all insertion operations to the enclosed ostringstream instance. The idea is to preserve type when doing cascading insertion operations. Normally the resulting type of a cascading insertion operation is ostream&, and any original type is lost. I'd like to maintain the type of the expression, because I want to be able to pass it to a method for further processing, and would like to have access to the underlying ostringstream to get at the string data that was inserted.

I'd like to be able to do something like the following, and have the result of the expression be LgStream:

LgStream() << 26 << " a plain string " << std::string("a std string") << someObjWithItsOwnInsertionOperator;

So I defined operator<<() for basic types, and template methods as a catch-all for everything else:

class LgStream
{
public:
    ostringstream m_oss;
    string str() {return m_oss.str();}

    LgStream &operator<<(int x) {m_oss << x; return *this;}
    LgStream &operator<<(long x) {m_oss << x; return *this;}
    LgStream &operator<<(char x) {m_oss << x; return *this;}
    LgStream &operator<<(bool x) {m_oss << (x ? 'T':'F'); return *this;}
    LgStream &operator<<(double x) {m_oss << x; return *this;}

    template <typename T>
    LgStream &operator<<(const T &x) {m_oss << x; return *this;}
    template <typename T>
    LgStream &operator<<(const T x) {m_oss << x; return *this;}

};

I don't have it right, and am getting "ambiguous overload" errors. For example:

LgStream() << "This is a plain string";

error: ambiguous overload for 'operator<<' in 'LgStream() << "This is a plain string"'
note: candidates are:
note: LgStream& LgStream::operator<<(int) <near match>
note:   no known conversion for argument 1 from 'const char [23]' to 'int' 
note: LgStream& LgStream::operator<<(long int) <near match>
note:   no known conversion for argument 1 from 'const char [23]' to 'long int'
note: LgStream& LgStream::operator<<(char) <near match>
note:   no known conversion for argument 1 from 'const char [23]' to 'char'

note: LgStream& LgStream::operator<<(bool)
note: LgStream& LgStream::operator<<(std::string)
note: LgStream& LgStream::operator<<(const T&) [with T = char [23], LgStream = LgStream]
note: LgStream& LgStream::operator<<(T) [with T = const char*, LgStream = LgStream]

Any help appreciated with either syntax, or if I am taking the wrong approach.

2 Answers2

0

Why don't you derive your class from std::basic_ostringstream? That way you don't have to define your own insertion operators and can use all the standard insertion operators.

References will say it's dangerous to derive from STL classes and it's best to be avoided. AFAIK, this is only a concern if you're going to use the derived class polymorphically by referring to its base class. To explain, since none of the STL classes declare their dtors as virtual, destroying a polymorphic object pointer would not invoke your derived class dtor and can result in memory leak. As long as you don't use LgStream this way, you should be ok.

Hari Mahadevan
  • 920
  • 7
  • 14
0

The primary source of ambiguity comes from the two template functions. One takes a "const T &x" and the other takes a "const T x". Any const T value will qualify for the reference version as well. Get rid of one of them. I compiled this code in VC++, after deleting the definition for "operator<<(const T x)", and it compiled without errors. Maybe your compiler will do the same.

Christopher Oicles
  • 3,017
  • 16
  • 11