Recursive vector template - c++

I've written the following piece of code:
template<typename T>
ostream& operator<<(ostream& os, const vector<T>& v) {
os << "{";
for (auto i=v.begin(); i!=v.end(); ++i) {
os << *i << " ";
}
os << "}";
return os;
}
This works fine for regular vector<int> instances, but what I want to do is this:
vector<vector<int> > v={{1,2},{3,4}}
cout << v; // Should print {{1 2 } {3 4 } }
Instead, I get compilation errors (the following text, with a long list of candidates): test.cpp|212|error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'std::vector<std::vector<int> >')|
I'd have thought the templated function could be used twice, recursively. Am I wrong? If not, what gives? If so, is there some way to make this generic without duplicating code?

#include <iostream>
#include <vector>
template<class T, class A>
std::ostream& operator<<(std::ostream& os, const std::vector<T,A>& v) {
os << "{";
for(auto&&e:v)
os<<e<<" ";
os << "}";
return os;
}
int main(){
std::vector<int> v1{1,2,3};
std::cout<<v1<<"\n";
std::vector<std::vector<int>> v2{{1},{2,3}};
std::cout<<v2<<"\n";
}
the above compiles and runs. Fix your typos, or be careful what namespace you are working with. Overloading operators in anything but the current namespace or in an ADL related one tends to fail.

Related

Define a template operator<< for iterables

I'm able to define and use:
std::ostream& operator<<(std::ostream& os, std::vector<int> const& container)
{
for (auto const& n : container)
os << n << ", ";
return os;
}
int main()
{
std::vector<int> data{0,1,2};
std::cout << data << '\n';
}
(demo)
But the definition of that operator doesn't depend on what kind of container I use. From there, I'd like to define a templated version:
template<class Iterable>
std::ostream& operator<<(std::ostream& os, Iterable const& iterable)
{
for (auto const& n : iterable)
os << n << ", ";
return os;
}
int main()
{
std::vector<int> data{0,1,2};
std::cout << data << '\n';
}
(demo)
This is where my compiler gets angry and verbosily reject it:
error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'char')
... with a lot of possible candidates.
Why is it not legal and how could I define such an operator?
As stated in this other StackOverflow question, How do I fix “ambiguous overload” error when overloading operator<< (templated)?
, when defining operator<<(std::ostream&, T) for all T, you overload it for types where an existing operator<< exists. Hence the ambiguous call:
os << n << ", ";
^-- recursively calls itself? or calls the overload provided by the Standard Library?
The solution is to use SFINAE to ensure you define your overload only for iterable types. Since the definition of the range-based for loop is based on begin and end, we can use it to discriminate what is an Iterable:
template<class Iterable, class = std::void_t<decltype(begin(std::declval<Iterable>()))>>
std::ostream& operator<<(std::ostream& os, Iterable const& iterable)
{
for (auto const& n : iterable)
os << n << ", ";
return os;
}
(demo)
Now, std::cout << data calls your version and std::cout << '\n' calls the build-in overload since the substitution fails for Iterable = char: begin(char) is not defined.

Overload for ostream on vectors throws error when using std::copy

I am wondering why the following fails to compile:
#include <vector>
#include <ostream>
#include <iterator>
#include <algorithm>
#include <iostream>
template <template <typename...> class Container, class T>
std::ostream& operator<<(std::ostream& oss, const Container<T>& c) {
oss << "[";
std::copy(std::cbegin(c), std::prev(std::cend(c)), std::ostream_iterator<T>(oss, ","));
return oss << (*std::crbegin(c)) << "]";
}
auto main() -> int {
std::vector<std::vector<unsigned>> data(5);
std::cout << data << std::endl;
return 0;
}
http://coliru.stacked-crooked.com/a/431617423f92ba4e
It compiles fine when I do one of the following:
Remove the line with std::copy
Change the vector to be a one-dimensional vector (std::vector<unsigned> data(5) for example).
What is it about std::copy that is causing the error?
Debugging with clion, this is the type of the nested vector that gets printed in the crbegin line:
Because your operator<< is not visible to std entities.
Note std::ostream_iterator<T> outputs values as if through operator<<, and according to [temp.dep.res]/1:
In resolving dependent names, names from the following sources are
considered:
Declarations that are visible at the point of definition of the template.
Declarations from namespaces associated with the types of the function arguments both from the instantiation context ([temp.point])
and from the definition context.
... your operator<< is neither visible at the point of definition of std::ostream_iterator<T>, nor in the namespace std, so the operator<< used in std::ostream_iterator<T> cannot be correctly resolved.
Your operator<< is not visible to std::ostream_iterator, so it can't call your operator<< on elements of the input container. Just use a manual loop instead, then it will work as expected.
Also, std::prev(std::cend(c)) and *(c.crbegin()) are undefined when the container is empty, so watch out for that.
Also, std::vector (like most other standard containers) has more than 1 template parameter, so use typename... Ts instead of class T in your operator's template parameters.
Try this:
#include <vector>
#include <iterator>
#include <iostream>
template < template<typename...> class Container, typename... Ts>
std::ostream& operator<<(std::ostream& oss, const Container<Ts...>& c) {
oss << "[";
if (!c.empty()) { // use std::empty() in C++17 and later
auto last = std::prev(std::cend(c));
/*
using value_type = typename Container<Ts...>::value_type;
std::copy(std::cbegin(c), last, std::ostream_iterator<value_type>(oss, ","));
*/
for(auto iter = std::cbegin(c); iter != last; ++iter)
oss << *iter << ",";
oss << *last;
}
return oss << "]";
}
int main() {
std::vector<std::vector<unsigned>> data(5);
std::cout << data << std::endl;
return 0;
}
Output
[[],[],[],[],[]]
Live Demo
Here's an experiment that will lead you toward the answer:
#include <vector>
#include <ostream>
#include <iterator>
#include <algorithm>
#include <iostream>
namespace std {
template <template <typename...> class Container, class T>
std::ostream& operator<<(std::ostream& oss, const Container<T>& c) {
oss << "[";
std::copy(std::cbegin(c), std::prev(std::cend(c)), std::ostream_iterator<T>(oss, ","));
return oss << (*std::crbegin(c)) << "]";
}
}
int main() {
std::vector<unsigned> t{ 1, 2, 3 };
std::vector<std::vector<unsigned>> data(5, t);
std::cout << data << std::endl;
return 0;
}
Sidenote: without defining t, it'll compile (i.e., your operator<< will be found when needed), but if you try to run it, it'll crash--trying to use std::prev(std::cend(c)) on an empty container isn't going to end well.
Of course, this does violate the requirements of the standard (defining your operator<< inside namespace std like this isn't allowed), but at least with typical compilers, the name will now be found so the code will compile.

error: 'e' is not a class, namespace, or enumeration

I'm trying to overload operator << for printing only every two elements of STL containers. But I have an error during the compilation:
error: 'e' is not a class, namespace, or enumeration
And here is my code:
#include <iostream>
#include <vector>
template<typename T>
std::ostream& operator<<(std::ostream &out, T const &e){
for(e::iterator it = e.begin(); it != e.end(); it = it + 2){
out << *it << " ";
}
return out;
}
int main(){
std::vector<int> v;
for(int i= 0; i < 10; i++){
v.push_back(i);
}
std::cout << v;
return 0;
}
You have two issues here.
One is with e::iterator. You can't access a member type through an object, you need to use the type. Instead you should just use auto it = e.begin(). If you can't use C++11, then you'll need to use
typename T::const_iterator it = e.begin()
The typename is needed because the name is dependent on a template parameter, and the const_iterator is needed instead of just iterator, because the parameter is marked const.
However, your more egregious error is in making this overload in the first place.
template<typename T>
std::ostream& operator<<(std::ostream &out, T const &e){
This declares an overload for std::ostream output for any type. This is sure to cause you a headache, and, sure enough, if you fix the first error, you get an ambiguous function call error when trying to output " ":
main.cpp:7:20: error: use of overloaded operator '<<' is ambiguous (with operand types '__ostream_type' (aka 'basic_ostream<char, std::char_traits<char> >') and 'const char [2]')
out << *it << " ";
~~~~~~~~~~ ^ ~~~
If you really want to make this work with every standard library container, I guess you check if something like T::iterator exists, and only have your overload enabled if that's true. Something like this:
template<typename T, typename = typename T::iterator>
std::ostream& operator<<(std::ostream &out, T const &e){
for(auto it = e.begin(); it != e.end(); it = it + 2){
out << *it << " ";
}
return out;
}
Live demo

Compile error when overloading operator<< with template arguments

I'm trying to use the stl copy () to print the key-value pair in a map. The code is as follows:
#include <iterator>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
//compile error if I comment out "namespace std"
namespace std {
template<typename F, typename S>
ostream& operator<<(ostream& os, const pair<F,S>& p) {
return os << p.first << "\t" << p.second << endl;
}
}
int main() {
map<int, int> m;
fill_n(inserter(m, m.begin()), 10, make_pair(90,120));
copy(m.begin(), m.end(), ostream_iterator<pair<int,int> >(cout,"\n"));
}
I'm trying to overload operator<<. The problem is that the code won't compile unless I surround the definition of the overloaded operator<< with namespace std. I think it is due to the name lookup mechanism of C++, which I still have trouble understanding. Even if I define non-template version like this:
ostream& operator<<(ostream& os, const pair<int,int>& p) {
return os << p.first << "\t" << p.second << endl;
}
It still won't compile. Can anyone explain why?
Your problem is with argument-dependent name lookup (ADL). The compiler is searching for an implementation of operator<< in namespace std, as both ostream and pair are in that namespace. You should make a wrapper that forwards to operator<< from the correct namespace:
template<class T>
struct ostreamer {
ostreamer(const T& value) : reference(value) {}
const T& reference;
friend ostream& operator<<(ostream& stream, const ostreamer& value) {
return stream << value.reference;
}
};
Then just use ostream_iterator<ostreamer<pair<const int, int>>> instead of ostream_iterator<pair<int, int>>. Note that because ostreamer stores by reference and not by value, you can’t rely on the implicit conversion from pair<const int, int> to pair<int, int> anymore. You could change ostreamer to store by value, but as it is, it has no overhead, and I think it’s better to be explicit anyway.

Overloading << for type inside another class

I have a typedef inside a class and I would like to overload the operator<< for it to be able to print it in ostream. However, the compiler cannot find the overloaded operator. How can I declare it so that it works?
#include <iostream>
#include <set>
using namespace std;
template <class C>
struct K {
typedef std::set<C> Cset;
Cset s;
// and many more elements here
friend ostream& operator<<(ostream& oo, const Cset& ss){
typename Cset::const_iterator it=ss.begin();
oo << "[";
for(; it!=ss.end(); ++it) oo << (*it) << ",";
oo << "]";
return oo;
}
void DoSomething(){
// do something complicated here
cout << s << endl;
// do something complicated here
}
};
int main(){
K <int> k;
k.s.insert(5);
k.s.insert(3);
k.DoSomething();
}
gcc version 4.4.5 20101112 (Red Hat 4.4.5-2) (GCC)
When a friend function is defined inline and there is no forward declaration outside the class, it is only found by ADL. However, your overload will never be found by ADL as it does not involve K arguments (note that K<int>::CSet is a typedef for std::set<C>).
Just for completeness: final version of the code for operator<<:
template <class T, class U>
std::ostream& operator<<(std::ostream& oo, const std::set <T,U> & ss){
typename std::set <T,U> ::const_iterator it=ss.begin();
oo << "[";
if(it!=ss.end()) oo << (*it++);
while(it!=ss.end()) oo << "," << (*it++);
oo << "]";
return oo;
}