How to overload operator << to act like what ostream does - c++

I am on implementing a class, and I'd like to pass some parameters to the instance using <<.
For example,
terminal term;
term << "Hello World!" << '\n';
The code goes below,
class terminal {
template <typename T>
terminal& operator << (T& t) {
std::cout << t;
return *this;
}
};
Basically, I'd like to be a stream instead of being the part of stream. (not cout << term;)
(Sorry for that I forgot to specify my question)
The question is, it worked well with strings, but it compiled failed if there is a number (like int, char, etc).
If we use the example above, the compiler will complain that
Invalid operands to binary expression ('terminal' and 'int')

I would change to the following, in order for sequencing of operator<< (e.g., term << "hello" << std::endl; ) to work:
namespace foo {
class terminal {
std::ostream &strm;
public:
terminal(std::ostream &strm_) : strm(strm_) {}
terminal() : strm(std::cout) {}
template <typename T>
friend std::ostream& operator<<(terminal &term, T const &t);
};
template <typename T>
std::ostream& operator<<(terminal &term, T const &t) {
term.strm << t;
return term.strm;
}
}
Live Demo

The problem is that your operator << takes its argument by reference to non-const, which means it can only bind to lvalues. So things like non-string literals are out. If you do not need to modify the argument, take it by const & instead. The language has a special rule which allows lvalue references to const to bind to rvalues as well.
terminal& operator << (const T& t) {
std::cout << t;
return *this;
}
If you do need to modify the argument, use a different approach than <<. A stream-like interface which would modify the streamed arguments during << would be extremely counterintuitive and a maintenance nightmare.

Related

Function call ambiguity in templatized code

Here is a (very distilled) use case and code example (sorry if it doesn't look too minimal, I couldn't figure out what else to exclude. The complete 'just compile' code can be found here: https://gcc.godbolt.org/z/5GMEGKG7T)
I want to have a "special" output stream, which, for certain user types, does special treatment during stream processing.
This special stream should be able to stream a "special" type with "special" treatment, as well as any other type which could be streamed through conventional streams - in the latter case, we simply use the conventional stream directly
struct Type { }; // Special type
struct Stream { // Special stream
// In this distilled example, we could achieve the same result
// by overriding << for ostream and Type, but the actual use case is wider
// and requires usage of the special stream type
// For illustration purposes only, we use this overload to cout "T!" for Types
Stream& stream(Type t) { std::cout << "T!"; return *this;}
// For any type for which cout << T{} is a valid operation, use cout for streaming
template<class T>
auto stream(const T& t) -> decltype(std::cout << std::declval<T>(), std::declval<Stream&>())
{
std::cout << t; return *this;
}
};
// Consumes every T for which Stream.stream is defined - those would be Type
// as well as anything else which can be sent to cout
template<class T>
auto operator<<(Stream& s, const T& t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
This code works as expected:
void foo()
{
Stream stream;
stream << 10 << Type{};
}
Now I want to define a different struct, which has a member of Type in it, and a streaming operator for the same struct:
struct Compound {
Type t;
int i;
};
// We want the `<<` operator of the struct templated, because we want it to work with
// any possible stream-like object out there
template<class S>
S& operator<<(S& s, Compound comp)
{
s << comp.t << " " << comp.i;
return s;
};
Unfortunately, this doesn't work well. An attempt to use this << operator:
void bar(Compound comp)
{
Stream s;
s << comp;
}
Triggers an ambiguity:
error: ambiguous overload for 'operator<<' (operand
types are 'Stream' and 'Compound')
note: candidate: 'decltype (s.Stream::stream(t)) operator<<(Stream&, const T&)
note: candidate: 'S& operator<<(S&, Compound) [with S = Stream]'
That makes sense, since after defining templated operator for << for Compound, it could be streamed via cout, as well as through this new operator!
I can certainly work aorund this by making << for Compound non-templated, and instead use the special Stream as a stream argument - but I'd like to avoid it.
Is there a way I can preserve the templated << for Compound and still avoid the ambiguity?
So you need to lower the priority of operator<<(Stream& s, const T& t) to make it the last resort.
You can do it by making it (seemingly) less specialized, by templating the first parameter:
auto operator<<(std::same_as<Stream> auto &s, const auto &t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
The change I made to the second parameter is unimportant, it's just for terseness.
Some extra boilerplate maybe?
template<class T> struct IsDefault: std::true_type {};
template<class T> concept Default = IsDefault<T>::value;
struct Stream {
template<Default T> Stream &operator<<(T &&t) {
return std::cout << t, *this;
}
};
struct Compound {};
template<> struct IsDefault<Compound>: std::false_type {};
Stream &operator<<(Stream &s, Compound &&c) { return s << "Compound"; }

Templated ostream operator << for multiple types within a namespace

I have a namespace with several structs and enum classes inside of it. For each type, I have a toString() method. Here is a small example:
namespace test {
struct A {
int i;
};
struct B {
float j;
};
std::string toString(const A &a){
return to_string(a.i);
}
std::string toString(const B &b){
return to_string(b.j);
}
}
I want to provide a templated operator<< which captures only these types, but not for types outside of this namespace:
template<class T>
std::ostream & operator<<(std::ostream &out, const T &t){
out << toString(t);
return out;
}
However, this gives me the following compilation error:
error: ambiguous overload for 'operator<<' (operand types are 'std::stringstream {aka std::__cxx11:basic_stringstream<char>}' and 'const char*')
How can I write a templated operator overload for this?
I solved it using concept & requires of C++20 (gcc >= 10.1):
template <typename T>
concept HaveToString = requires (T t) {
{ toString(t) };
};
template<HaveToString T>
std::ostream & operator<<(std::ostream &out, const T& t){
out << toString(t);
return out;
}
int main() {
test::A a;
std::cout << a << std::endl;
return EXIT_SUCCESS;
}
EDIT
For C++11:
template<typename T, typename = decltype(toString(std::declval<T>()))>
std::ostream & operator<<(std::ostream &out, const T& t){
out << toString(t);
return out;
}
Or as #MooingDuck mentioned in the comments:
template<typename T>
auto operator<<(std::ostream &out, const T& t) -> decltype(out<<toString(t)) {
out << toString(t);
return out;
}
Explanations
First of all, a really good article about unevaluated operands. It will help to understand what is going on in the expressions: decltype(toString(std::declval<T>())) and decltype(out<<toString(t)) which are both basically doing the same thing-> Setting a rule that any call to this function, have to support the call to toString function with the T parameter type.
First Approach
decltype(toString(std::declval<T>()))
Let's split this complex expression into sub expressions, from the inside out:
decltype(toString( std::declval<T>() ))
std::declval<T>() In some very simple words - means that we are "assuming" we created a variable of the type T at a compile time (If you didn't read the article yet, now it's a really good time to do so). The important thing to know before continue- we didn't do it, the important word is assuming.
decltype( toString(std::declval<T>()) )
The magic continue all the way up to decltype which checking for the type of the unevaluated expression within it. So, if toString that calls T type variable exists, it will return the value that toString function returns. If this function doesn't exist, a compile time error will be thrown (or in this context, the compiler won't deduce this function for the given type).
typename = decltype(toString(std::declval<T>()))
This section in the template meant to enable this function whenever the type returning from decltype is legal.
#MooingDuck Approach
auto operator<<(std::ostream &out, const T& t) -> decltype(out<<toString(t)) { /*...*/ }
Return value: auto
C++11: Deduced by the expression the after the operator ->.
After C++14: Calculated at compile time by the return expression inside the function (if there is no return expression, the return value deduced at compile time to void).
-> decltype(out<<toString(t))
Define the return value type.
decltype(out<<toString(t))
As explained before, whatever comes inside decltype is unevaluated expression. The compiler won't evaluate this expression, but it will make sure that the expression can be evaluated at runtime (or else an exception will be thrown, or in this case, the compiler won't deduce this function), and it will return the type of the returned value from this expression.

std::forward with templated overloaded function

I've got no idea why compiler gives me warnings about template instantiations.
Thats a piece of code which runs just fine and outputs lvalue/rvalue properly:
//template<typename T>
void overloaded(const /*T*/std::string& in)
{
std::cout << "lvalue" << std::endl;
}
//template<typename T>
void overloaded(/*T*/std::string&& in)
{
std::cout << "rvalue" << std::endl;
}
template<typename T>
void pass(T&& in)
{
overloaded(std::forward<T>(in));
}
int main()
{
std::string a;
pass(a);
pass(std::move(a));
getchar();
}
But i need to use it with templated type. So modifying the "overloaded" functions to
template<typename T>
void overloaded(const T& in)
{
std::cout << "lvalue" << std::endl;
}
template<typename T>
void overloaded(T&& in)
{
std::cout << "rvalue" << std::endl;
}
Gives template instantiations warnings, (when to me its clear T should be std::string), and console outputs rvalue 2 times instead of lvalue first.
What am i doing wrong?
Templates such as T&& are special. They are called "forwarding references". They have special deduction rules for functions like:
template<typename T>
void overloaded(T&& in)
Assume for a moment that overloaded is not overloaded. If you pass an lvalue expression of type std::string to overloaded, T will deduce as std::string&. If you pass an rvalue expression of type std::string to overloaded, T will deduce to std::string. You can use this knowledge to do this:
template<typename T>
void overloaded(T&& in)
{
if (std::is_lvalue_reference<T>::value)
std::cout << "lvalue" << std::endl;
else
std::cout << "rvalue" << std::endl;
}
In general, it is an anti-pattern to overload T&& templates with anything else. These special templates come in handy when you want to catch everything.

Prohibition of operator << calling

I have some code.
#include <iostream>
template<typename T>
struct Test
{
Test(bool v):flg(v) { }
void func() { }
typedef void (Test::*unspecified)();
operator unspecified() const
{
return flg ? &Test::func : 0;
}
bool flg;
};
template<typename T>
std::ostream& operator << (std::ostream&, typename Test<T>::unspecified);
int main()
{
Test<int> t(true);
std::cout << t << std::endl;
}
Output is
1
It works fine, but i want to get undefined reference. If Test is not template class i get undefined reference. So, why compiler not use operator << for function type and do standart conversion from pointer to class-member to bool?
In typename Test<T>::unspecified, T is in a non-deducible context, since it appears to the left of a ::. Thus your function template is never even considered, and the conversion to unspecified is used as the sole viable overload.
The short answer is simply that "templates don't work like that". Let me know if you want a longer answer.

std::ostringstream operator overload search order?

I have the following class:
namespace {
class MimeLogger : public std::ostringstream
{
public:
MimeLogger()
{}
~MimeLogger()
{
LOGEVENT( logModuleWSE, logEventDebug, logMsgWSETrace1, str() );
}
};
}
When I do this:
MimeLogger() << "Hello " << "World";
The first "Hello " string is treated as a void*. If I debug the code, "Hello " is passed into std::basic_ostream::operator<< (void const*) and prints as a pointer value, not a string. The second string, "World" is properly passed into the global overloaded << operator that takes a char const*.
I expect both usages of the << operator to resolve to the same overload, but this does not happen. Can someone explain, and maybe propose a fix?
Thanks in advance.
Update
I neglected to mention that I'm stuck with C++03, but I'm glad that some people covered both the C++03 and C++11 cases.
C++03: For the expression MimeLogger() << "Hello ", the template function
template <typename charT, class traits>
std::basic_ostream<charT, traits>& std::operator<< (
std::basic_ostream<charT, traits>& os,
const char* cstr);
is not considered during overload resolution because the temporary MimeLogger() may not be bound to a non-const reference. The member function overloads do not have this problem because the rules for the implicit parameter do allow binding to a temporary.
If you can use a compiler with support for C++11 rvalue-references, this should work as you intended, because the C++11 library provides an additional overload
template <typename charT, class traits, typename T>
std::basic_ostream<charT, traits>& std::operator<< (
std::basic_ostream<charT, traits>&& os,
const T& x ); // { os << x; return os; }
which allows temporary streams to be used left of << as though they were not temporary.
(I did try a test program with g++ and got different results without and with -std=c++0x.)
If you cannot use a C++11 friendly compiler, adding this to the public section of class MimeLogger is a workaround that will do what you want with C++03:
template<typename T>
MimeLogger& operator<<(const T& x)
{
static_cast<std::ostringstream&>(*this) << x;
return *this;
}
using std::ostringstream::operator<<;
The using-declaration makes sure the member overloads from the standard library are also visible from MimeLogger. In particular, without it manipulators like std::endl don't work with the template operator, since std::endl is itself a function template, and that's too much template type deduction to expect from C++. But things are fine as long as we're sure not to hide the ostream member that makes the function manipulators work (27.7.3.6.3):
namespace std {
template <typename charT, class traits>
class basic_ostream : /*...*/ {
public:
basic_ostream<charT, traits>& operator<<(
basic_ostream<charT,traits>& (*pf)(basic_ostream<charT,traits>&));
};
}
How about using containment of std::ostringstream ?
class MimeLogger
{
private:
std::ostringstream oss_m;
public:
MimeLogger()
{
}
~MimeLogger()
{
std::cout << __FILE__ << "(" << __LINE__ << "):" << oss_m.str() << "\n";
}
template<typename Type>
MimeLogger& operator<<(const Type& t)
{
oss_m << t;
return *this;
}
};
void LogDemo()
{
MimeLogger logger;
logger << "Hello " << "World!!\n";
MimeLogger() << "Hello " << "StackOverflow!!\n";
}