How to write an insertion operator function template? - c++

I'm trying to write a single function template instead of a bunch of similar overloads for the insertion operator. The redundant overloaded versions work, but when I try to unite them in a single function template, the compiler complains of ambiguity. For example:
#include <iostream>
#include <list>
class fooBar
{
public:
fooBar(int iVal): iValue(iVal) {}
int getValue() {return iValue;}
private:
int iValue;
};
class foo
{
public:
foo()
{
for(int i = 0; i < 64; i++)
lstFooBars.push_back(fooBar(i));
}
std::list<fooBar>& getList()
{
return lstFooBars;
}
private:
std::list<fooBar> lstFooBars;
};
class bar
{
public:
bar()
{
for(int i = 0; i < 64; i++)
lstFooBars.push_back(fooBar(i));
}
std::list<fooBar>& getList()
{
return lstFooBars;
}
private:
std::list<fooBar> lstFooBars;
};
std::ostream& operator<<(std::ostream& osOut, fooBar& fbrFooBar)
{
osOut << fbrFooBar.getValue();
return osOut;
}
template <typename T> std::ostream& operator<<(std::ostream& osOut, T& tContainer)
{
for(fooBar fbrFooBar: tContainer.getList())
osOut << "[" << fbrFooBar << "] ";
return osOut;
}
int main()
{
foo fooFoo;
bar barBar;
std::cout << std::endl << fooFoo << std::endl << std::endl;
std::cout << std::endl << barBar << std::endl << std::endl;
return 0;
}
...and the compiler tells me that:
test.cpp: In function ‘std::ostream& operator<<(std::ostream&, T&)’:
test.cpp:63:9: error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘const char [2]’)
63 | osOut << "[" << fbrFooBar << "] ";
| ~~~~~ ^~ ~~~
| | |
| | const char [2]
| std::ostream {aka std::basic_ostream<char>}
Why does it work when you overload the same function over and over for each case and it doesn't compile like this? What am I missing here?

You've inadvertedly added a possible overload for const char* by making this:
template<typename T>
std::ostream& operator<<(std::ostream& osOut, T& tContainer)
If you narrow it down a bit with SFINAE, it should work.
This overload will only work for types with a getList() member function for example:
template<typename T, typename U = decltype(std::declval<T>().getList())>
std::ostream& operator<<(std::ostream& osOut, T& tContainer)

operator<< by default takes chars as argument, not string literals (inside "s) https://www.cplusplus.com/reference/ostream/ostream/operator-free/.
So, in order to make the call in the code you provide not ambiguous, you should try either to use single chars, or std::string:
#include <iostream>
#include <list>
#include <string>
class fooBar
{
public:
fooBar(int iVal): iValue(iVal) {}
int getValue() {return iValue;}
private:
int iValue;
};
class foo
{
public:
foo()
{
for(int i = 0; i < 64; i++)
lstFooBars.push_back(fooBar(i));
}
std::list<fooBar>& getList()
{
return lstFooBars;
}
private:
std::list<fooBar> lstFooBars;
};
class bar
{
public:
bar()
{
for(int i = 0; i < 64; i++)
lstFooBars.push_back(fooBar(i));
}
std::list<fooBar>& getList()
{
return lstFooBars;
}
private:
std::list<fooBar> lstFooBars;
};
std::ostream& operator<<(std::ostream& osOut, fooBar& fbrFooBar)
{
osOut << fbrFooBar.getValue();
return osOut;
}
template <typename T> std::ostream& operator<<(std::ostream& osOut, T& tContainer)
{
for(fooBar fbrFooBar: tContainer.getList())
//osOut << std::string("[") << fbrFooBar << std::string("] "); // solution 1: use std::string
osOut << '[' << fbrFooBar << ']' << ' '; // solution 2: use single chars
return osOut;
}
int main()
{
foo fooFoo;
bar barBar;
std::cout << std::endl << fooFoo << std::endl << std::endl;
std::cout << std::endl << barBar << std::endl << std::endl;
return 0;
}

Related

Why cannot c++ compiler find operator<<

Why cannot compiler find operator<<. Where is the compiler looking into to find the definition of operator<< when it encounters the line
cout <<f.some_func()<<endl;
The error:
error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘std::vector<std::vector<int> >’)
cout <<f.some_func()<<endl;
some notes:....
error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
cout <<f.some_func()<<endl;
#include <iostream>
#include <string>
#include<vector>
using namespace std;
struct Bar
{
int y;
};
class Foo
{
int x;
friend ostream & operator<<(ostream & os, Foo& f)
{
os << "Foo: " << f.x << endl;
return os;
}
friend ostream & operator<<(ostream & os, Bar& b)
{
Foo f;
os << "Bar b.y: " << b.y << endl;
os << "Bar f.x: " << f.x << endl;
return os;
}
friend ostream & operator<<(ostream & os, vector<vector<int> > const& vec ){
os << 5;
return os;
}
public:
Foo(int x = 10):x{x}{}
vector<vector<int> > some_func(){
vector<vector<int> > abc(3, vector<int>(3));
int x = 900;
return abc;
}
//If I do this
void wrapper(){
cout << this->some_func() << endl;
}
};
int main()
{
Bar b { 1 };
Foo f{ 13 };
cout << f << endl;
//cout << b << endl;
cout <<f.some_func()<<endl;
return 0;
}
Functions like this that are defined as friends inside the class have rather unusual name lookup rules. They're free functions, but their names are inside of the class of which they're a friend. Because of this, they're only found via argument-dependent lookup.
For that to work, however, at least one of the arguments has to be something like a reference to an object of the class in which it's defined, or else something defined inside that class.
In your case, the arguments are an iostream and a vector<vector<int>>, neither of which has any relationship to Foo, so the compiler doesn't use argument-dependent lookup to find the function inside of Foo.
The obvious way to make it work is to have the overloads for Foo and Bar inside the definitions of Foo and Bar, and the overload for std::vector<vector<int>> outside any class:
#include <iostream>
#include <string>
#include<vector>
using namespace std;
struct Bar
{
int y;
friend ostream & operator<<(ostream & os, Bar& b)
{
return os << "Bar b.y: " << b.y;
}
};
class Foo
{
int x;
friend ostream & operator<<(ostream & os, Foo& f)
{
return os << "Foo: " << f.x;
}
public:
Foo(int x = 10) : x{ x } {}
vector<vector<int> > some_func() {
vector<vector<int> > abc(3, vector<int>(3));
int x = 900;
return abc;
}
};
ostream & operator<<(ostream & os, vector<vector<int> > vec) {
return os << 5;
}
int main()
{
Bar b{ 1 };
Foo f{ 13 };
cout << f << '\n';
cout << b << '\n';
cout << f.some_func() << '\n';
return 0;
}

invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ostream')

I'm trying to do
cout << Print(cout); However, there is an "invalid operands to binary expression ('ostream' (aka 'basic_ostream') and 'ostream')" error when compiling.
#include <iostream>
using namespace std;
ostream& Print(ostream& out) {
out << "Hello World!";
return out;
}
int main() {
cout << Print(cout);
return 0;
}
Why this doesn't work?
How can I fix this? Thanks!!
The syntax you might be looking for is std::cout << Print << " and hello again!\n";. The function pointer is treated as a manipulator. A built-in operator << takes the pointer to Print and calls it with cout.
#include <iostream>
using namespace std;
ostream& Print(ostream& out) {
out << "Hello World!";
return out;
}
int main() {
cout << Print << " and hello again!\n";
return 0;
}
Here is your second request:
#include <iostream>
#include <vector>
#include <iterator>
template <class Argument>
class manipulator
{
private:
typedef std::ostream& (*Function)(std::ostream&, Argument);
public:
manipulator(Function f, Argument _arg)
: callback(f), arg(_arg)
{ }
void do_op(std::ostream& str) const
{
callback(str, arg);
}
private:
Function callback;
Argument arg;
};
template <class T>
class do_print : public manipulator<const std::vector<T>&>
{
public:
do_print(const std::vector<T>& v)
: manipulator<const std::vector<T>&>(call, v) { }
private:
static std::ostream& call(std::ostream& os, const std::vector<T>& v)
{
os << "{ ";
std::copy(v.begin(), v.end(),
std::ostream_iterator<T>(std::cout, ", "));
return os << "}";
}
};
template <class Argument>
std::ostream& operator<<(std::ostream& os, const manipulator<Argument>& m)
{
if (!os.good())
return os;
m.do_op(os);
return os;
}
template<class T>
do_print<T> Print(const std::vector<T>& v)
{
return do_print<T>(v);
}
int main()
{
std::vector<int> v{1, 2, 3};
std::cout << Print(v);
}

How to write a function wrapper for cout that allows for expressive syntax?

I'd like to wrap std::cout for formatting, like so:
mycout([what type?] x, [optional args]) {
... // do some formatting on x first
std::cout << x;
}
and still be able to use expressive syntax like
mycout("test" << i << endl << somevar, indent)
instead of being forced to be more verbose like
mycout(std::stringstream("test") << i ...)
How can I implement this? What type to make x?
Edit: added consideration for optional arguments
How about this:
struct MyCout {};
extern MyCout myCout;
template <typename T>
MyCout& operator<< (MyCout &s, const T &x) {
//format x as you please
std::cout << x;
return s;
}
And put MyCout myCout; into any one .cpp file.
You can then use myCout like this:
myCout << "test" << x << std::endl;
And it will call the template operator<< which can do the formatting.
Of course, you can also provide overloads of the operator for special formatting of specific types if you want to.
EDIT
Apparently (thanks to #soon), for standard manipulators to work, a few more overloads are necessary:
MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ostream &)) {
f(std::cout);
return s;
}
MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ios &)) {
f(std::cout);
return s;
}
MyCout& operator<< (MyCout &s, std::ostream& (*f)(std::ios_base &)) {
f(std::cout);
return s;
}
EDIT 2
I may have misunderstoor your original requirements slightly. How about this (plus the same manipulator overloads as above):
struct MyCout
{
std::stringstream s;
template <typename T>
MyCout& operator << (const T &x) {
s << x;
return *this;
}
~MyCout() {
somehow_format(s);
std::cout << s.str();
}
};
int main() {
double y = 1.5;
MyCout() << "test" << y;
}
This comes easy with variadic template arguments:
template <class T>
void print(T t)
{
std::cout << t;
}
template <class T, class... Args>
void print(T t, Args... args)
{
std::cout << t << std::endl;
print(args...);
}
int main()
{
std::cout << std::boolalpha;
print(3, 's', true, false);
}
Output:
3
s
true
false
Live Demo
A variation from the answers:
#include <iostream>
using namespace std;
class MyCout
{
public:
MyCout& operator()(bool indent) {
if ( indent ) cout << '\t';
return *this;
}
template<class T>
MyCout& operator<<(T t) {
cout << t;
return *this;
}
MyCout& operator<<(ostream& (*f)(ostream& o)) {
cout << f;
return *this;
};
};
int main()
{
MyCout mycout;
int x = 10;
mycout(true)<< "test" << 2 << x << endl ;
}
You can use this kind of class:
#include <iostream>
using namespace std;
class CustomOut
{
public:
template<class T>
CustomOut& operator<<(const T& obj)
{
cout << " my-cout " << obj;
return *this;
}
};
int main()
{
CustomOut mycout;
mycout << "test" << 4 << "\n" << 3.4;
}
You'd need more code to use std::endl and other functors, so i've used here simple \n instead.

Template specialization for a compile-time constant integer

Is it possible to specialize a function (or a class at least) to select between constant (compile-time!) integer and all other arguments? And if so, it would be nice to specialize (enable_if) for specific constant values only.
In the example below this would mean output "var", "const", and "var", not three "var"s.
#include <type_traits>
#include <iostream>
using namespace std;
struct test
{
template <typename T>
test& operator=(const T& var) { cout << "var" << endl; return *this; }
template <int n> // enable_if< n == 0 >
test& operator=(int x) { cout << "const" << endl; return *this; }
};
int main()
{
test x;
x = "x";
x = 1;
int y = 55;
x = y;
return 0;
}
UPDATE: edited the code to emphasize that it has to be compile-time constant.
To get var, const, var in your example you can do that.
struct test {
template <typename T>
test& operator=(const T& var) { cout << "var" << endl; return *this; }
test& operator=(int &&x) { cout << "const" << endl; return *this; }
};
It will work for all temporaries, but it will fail for:
const int yy = 55;
x = yy;
To make it work for such a case too you will need to add:
test& operator=(int &x) { cout << "var" << endl; return *this; }
test& operator=(const int &x) { cout << "const" << endl; return *this; }

Why does writing to temporary stream fail?

Consider the following code:
#include <sstream>
#include <iostream>
class Foo : public std::stringstream {
public:
~Foo() { std::cout << str(); }
};
int main()
{
Foo foo;
foo << "Test1" << std::endl;
Foo() << "Test2" << std::endl;
return 0;
}
When I execute this, it gives me:
004177FC
Test1
I do not understand why the second example gives me gibberish. The temporary should live until the entire expression is evaluated, so why does it not behave the same as the first example?
I tested it.
I can guess that operator<< cannot bind a temporary to a non-const reference, so any externally defined operator<< functions will not work on the Foo temporary, but any class member ones will so if ostream or ostringstream has any internal operator<< members they will work.
Therefore it may be that the overload to a pointer is a member function whilst the special one for const char * is externally declared.
The non-temporary can bind to the non-const reference for the more specialist overload.
If you really need this you can workaround with a wrapper
class Foo :
{
mutable std::ostringstream oss;
public:
~Foo()
{
std::cout << oss.str();
}
template<typename T>
std::ostream&
operator<<( const T& t ) const
{
return oss << t;
}
};
Tested and works. The first operator<< will return you the underlying stream.
I tried this too but it coredumped:
class Foo : std::ostringstream
{
Foo & nonconstref;
public:
Foo() : nonconstref( *this ) {}
~Foo()
{
std::cout << str();
}
template<typename T>
std::ostream&
operator<<( const T& t ) const
{
return nonconstref << t;
}
};
This also works:
class Foo : public std::ostringstream
{
public:
Foo() {}
~Foo()
{
std::cout << str();
}
Foo& ncref()
{
return *this;
}
};
int main()
{
Foo foo;
foo << "Test1" << std::endl;
Foo().ncref() << "Test2" << std::endl;
}