2

I want to output a collection of collection (a vector of vectors in this case) by calling the outside collection once by operator<<

It works when I remove the ' ' from the operator<<() function, but I want there to be a space between every output element of every line. I tried replacing ' ' with " " (also included string header file) but getting the same error.

Is there a way to resolve this?

#include <iostream>
#include <vector>

using namespace std;

vector<vector<bool>> lookup(10, vector<bool>(10, true));

template <typename T>
ostream& operator<< (ostream& out, const T& collection)
{
    for (const auto& elem : collection)
        out << elem << ' ';
    return out << endl;
}

int main()
{
    cout << lookup << endl;
}

I'm getting the following error:

1>------ Build started: Project: test, Configuration: Debug Win32 ------
1>test.cpp
1>c:\users\user\source\repos\codechef\practice\beginner\test\test\test.cpp(16): error C2593: 'operator <<' is ambiguous
1>c:\users\user\source\repos\codechef\practice\beginner\test\test\test.cpp(13): note: could be 'std::ostream &operator <<<char>(std::ostream &,const T &)'
1>        with
1>        [
1>            T=char
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\ostream(921): note: or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,_Elem)'
1>        with
1>        [
1>            _Elem=char
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\ostream(834): note: or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)'
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\ostream(749): note: or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)'
1>c:\users\user\source\repos\codechef\practice\beginner\test\test\test.cpp(16): note: while trying to match the argument list '(std::ostream, char)'
1>c:\users\user\source\repos\codechef\practice\beginner\test\test\test.cpp(22): note: see reference to function template instantiation 'std::ostream &operator <<<std::vector<std::vector<bool,std::allocator<_Ty>>,std::allocator<std::vector<_Ty,std::allocator<_Ty>>>>>(std::ostream &,const T &)' being compiled
1>        with
1>        [
1>            _Ty=bool,
1>            T=std::vector<std::vector<bool,std::allocator<bool>>,std::allocator<std::vector<bool,std::allocator<bool>>>>
1>        ]
1>Done building project "test.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
srt1104
  • 959
  • 7
  • 11

1 Answers1

5

The problem is that T from your template is not limited to specific type or range of types. Compiler can replace it with any type it wants.

When you write out << ' '; compiler looks for a function ostream& operator<< (ostream& out, const char& collection) and it finds two such functions. One of them is from the standard library and the other is your function. Compiler is unable to decide which version it should use so it just stops the compilation.

To fix this problem you need to limit your template so it won't accept types that you don't need. One way of doing that would be to make a template which accepts only vector:

#include <iostream>
#include <vector>

using namespace std;

vector<vector<bool>> lookup(10, vector<bool>(10, true));

template <typename T>
ostream& operator<< (ostream& out, const vector<T>& collection)
{
    for (const auto& elem : collection)
        out << elem << ' ';
    return out << endl;
}

int main()
{
    cout << lookup << endl;
}

If you need to define this function for more types of containers, instead of copying it multiple times, you can create a template which accepts all types but it doesn't have a name that collides with standard library. Then you can create a few simple instances of operator<< which only call your universal function.

#include <iostream>
#include <vector>
#include <array>

using namespace std;

vector<vector<bool>> lookup(10, vector<bool>(10, true));

template <typename T>
ostream& printCollection (ostream& out, const T& collection)
{
    for (const auto& elem : collection)
        out << elem << ' ';
    return out << endl;
}

template <typename T>
ostream& operator<< (ostream& out, const vector<T>& collection)
{
    return printCollection(out, collection);
}

template <typename T, size_t N>
ostream& operator<< (ostream& out, const array<T, N>& collection)
{
    return printCollection(out, collection);
}

int main()
{
    cout << lookup << endl;
}

I think it would be even possible without defining function for each type of container separately. It would require some high level template magic, though. You can read this c++ template class; function with arbitrary container type, how to define it? to learn more about that.

Piotr Siupa
  • 3,929
  • 2
  • 29
  • 65
  • I'd prefer if it works for various combination of collections (like array, 10>). Is there a way to make it reject char collections(?) ? – srt1104 Apr 18 '19 at 14:28
  • @ShreyasRagit You can read this question: https://stackoverflow.com/q/874298/3052438 However I would advice to rather defines version of your function which accept other types. Restricting only certain types will lead to similar errors when you ever try something outside of the restricted scope. I'll expand my answer to show some example. – Piotr Siupa Apr 18 '19 at 14:34
  • Thanks! That's a nice workaround. I have another doubt related to this so I'll ask it here. When I run the code in the first part of your answer, I'm getting some weird indentations. The first line prints fine, but from second to the tenth, there is a space before the first element. I'm not able to figure out why. Can you try running it? – srt1104 Apr 18 '19 at 14:56
  • @ShreyasRagit This is caused by you adding space after every element of vector. It works relatively OK for simple vectors but it falls flat when you try to print vector of vectors because after printing every vector id adds additional space with happens to be at the beginning of the line because function ends with `endl`. You have to invent some more complicated algorithm if you want have everything aligned. – Piotr Siupa Apr 18 '19 at 15:05