Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could I print the contents of any container in a generic way?

I am trying to write a piece of code for fun using C++ templates.

#include <iostream>
#include <vector>

template <class Container>
std::ostream& operator<<(std::ostream& o, const Container& container)
{
    typename Container::const_iterator beg = container.begin();

    o << "["; // 1

    while(beg != container.end())
    {
        o << " " << *beg++; // 2
    }

    o << " ]"; // 3

    return o;
}

int main()
{
    std::vector<int> list;

    list.push_back(0);
    list.push_back(0);

    std::cout << list;

    return 0;
}

The above code doesn't compile :)

At 1, 2, 3 the same error is produced : error C2593: 'operator <<' is ambiguous

All what I am trying to do is overloading the << operator to work with any container. Does that make sense ? How would that be done If possible, if not why ?

EDIT :: Thanks for corrections :) 'sth' way is a good solution.

I am just curious if this ambiguity -as Neil explained- would go away if we could use C++0x Concepts ?

like image 826
Khaled Alshaya Avatar asked Jul 20 '09 15:07

Khaled Alshaya


3 Answers

You can restrict your operator<< to only apply to templated containers by specifying that the Container template parameter is itself templated. Since the C++ std containers also have an allocator template parameter you also have to include this as a template parameter of Container.

template
    < typename T
    , template<typename ELEM, typename ALLOC=std::allocator<ELEM> > class Container
    >
std::ostream& operator<< (std::ostream& o, const Container<T>& container)
{
    typename Container<T>::const_iterator beg = container.begin();

    o << "["; // 1

    while(beg != container.end())
    {
        o << " " << *beg++; // 2
    }

    o << " ]"; // 3

    return o;
}

int main()
{
    std::vector<int> list;

    list.push_back(0);
    list.push_back(0);

    std::cout << list;

    return 0;
}
like image 68
jon-hanson Avatar answered Nov 15 '22 17:11

jon-hanson


Your newly defined operator<< does not only match containers, but also any other types like ints and strings. That's why the compiler complains about ambiguities when it needs to find the matching operator<< to output "[".

One way to work around this problem would be to rename your output function:

template <class Container>
std::ostream& container_out(std::ostream& o, const Container &container) {
  // ...
}

You can then add simple wrappers to enable operator<< for all the containers you want to print:

template<typename T>
std::ostream& operator<<(std::ostream& o, const std::vector<T> &container) {
  return container_out(o, container);
}

template<typename T>
std::ostream& operator<<(std::ostream& o, const std::map<T> &container) {
  return container_out(o, container);
}
like image 22
sth Avatar answered Nov 15 '22 16:11

sth


What is the error? I saw one, you need a typename:

typename Container::iterator beg = container.begin();

What happens here is that the compiler doesn't know anything about Container when it is first reading it. So we have to give it a little help and tell it that iterator will be a type(syntactically it could be any valid name at class scope, so a function, variable,...). When writing a template method, any type that depends on the template type must specify that it is a type with the keyword typename.

like image 3
Matt Price Avatar answered Nov 15 '22 17:11

Matt Price