I'm writing a class that overloads the << and >> operator similar to std::istream and std::ostream. The first few functions worked as desired. However, I want to use other std::*stream classes inside my own class for formatting purposes, and this causes an interesting, I would say, 'recursive' compiler issue:
class SmartStream
{
public:
template <typename F> friend F &operator << (F &in, float f)
{
in.type ("f");
in.m_buf.str ("");
in.m_buf << f; // This causes a 'recursive' compile call
in.add (in.m_buf.str ());
return in;
}
protected:
std::ostringstream m_buf;
void type(const std::string &_type) {};
void add(const std::string &) {};
};
The actual error is long but it starts like this:
rec_stream.cpp: In instantiation of 'F& operator<<(F&, float) [with F = std::basic_ostringstream<char>]':
rec_stream.cpp:14:18: required from 'F& operator<<(F&, float) [with F = ExtraStream]'
rec_stream.cpp:60:11: required from here
rec_stream.cpp:12:9: error: 'class std::basic_ostringstream<char>' has no member named 'type'
in.type ("f");
^
So apparently the compiler applies the same overloaded << operator to the m_buf
variable of type std::ostringstream, but of course that doesn't have type() and add() functions. After reading the answer to this question that kinda makes sense, but doesn't provide a solution.
No before you say, use this in SmartStream:
class SmartStream
{
....
SmartStream &operator << (float f)
{
type("f");
m_buf.str ("");
m_buf << f; // Ambiguous overload
add( m_buf.str ());
return *this;
}
};
There are two problems. First, as noted in the code, that triggers an ambiguous overload. Second, consider the following:
class ExtraStream: public SmartStream
{
public:
template <typename F> friend F &operator << (F &in, struct tm t)
{
in.type ("tm");
in.m_buf.str ("");
in.m_buf << t.tm_hour; // Ambiguous overload
in.add (in.m_buf.str ());
return in;
}
};
Indeed, I am extending SmartStream to handle custom types using the mechanism laid out in that class. Using the non-friend operator will not allow me to do this:
ExtraStream es;
struct tm t1, t2;
float f1, f2;
es << f1 << t1 << f2 << t2; // works
Because after a << (float)
the return type is SmartStream which does not know how to handle struct tm
s.
My question:
Is there a way to convince the compiler (gcc 4.8.2) to use the 'base' << operator for std::ostringstream and not the overloaded one? I tried various casts, :: resolution operators, moving the code with in.m_buf << f;
to a non-templated function in SmartStream but nothing helped.
Also, why does it only use this inside the template operator << function? Any use of << on std::ostringstream outside of that function works as expected.