C++ generic overload of operator<< for STL containers produces ambiguous overload error with strings

I mean to write a generic overload of operator<< to print STL containers. I put together code below. Whenever operator<< involves a string, it produces a compilation error ambiguous overload for 'operator<<', in the example in problem lines 1 and 2.

How can I get rid of the error, without losing the generic overload, and without having to write explicit instantiations for every possible use of containers with strings? Perhaps excluding from my overload the strings.

#include <iostream>
#include <vector>
#include <set>
#include <list>
#include <map>
#include <tuple>
#include <string>

// Maximum number of printed values. After this, print "..."
#define MAX_PRINT_VALS 10

//=========================================================================
// Set of functions to dump STL containers

template <template <class...> class Container, class ...T>
std::ostream& operator<<(std::ostream& os, const Container<T...>& c)
{
    os << "[";

    size_t nvals = 0;
    for ( auto iter = c.begin() ; iter != c.end() ; iter++ ) {
        os << *iter;
        nvals++;
        if (iter != --c.end())
            os << ", ";
        if (nvals > MAX_PRINT_VALS) {
           os << "... (total of " << c.size() << " values)";
           break;
        }
    }

    os << "]";
    return os;
}

template<class Key, class T>
std::ostream& operator<<(std::ostream& os, const std::pair<Key, T>& p)
{
    os << "(" << p.first << ", " << p.second << ")";
    //os << std::endl;
    return os;
}


using namespace std;


int main(int argc, char **argv) {

    //============================================================
    // Print vector
    const size_t nmax = 3;
    vector<double const*> vec_dp;
    for (size_t n = 0; n < nmax; n++) {
        vec_dp.push_back(new double(n+1.5));
    }
    cout << "Vector of indices vec_dp = " << vec_dp << endl;
    for (size_t n = 0; n < nmax; n++) {
        delete vec_dp[n];
    }

    vector<string> vec_s;
    for (size_t n = 0; n < nmax; n++) {
        vec_s.push_back("asa");
    }
    cout << "Vector of string vec_s = " << vec_s << endl;         // PROBLEM LINE 1

    //============================================================
    // Print set
    set<double> set_d;
    for (size_t n = 0; n < nmax; n++) {
        set_d.insert(n+1.3);
    }
    cout << "Set of double set_d = " << set_d << endl;

    //============================================================
    // Print list
    list<double> list_d;
    for (size_t n = 0; n < (nmax + 10); n++) {
        list_d.emplace_back(n+1.4);
    }
    cout << "List of double list_d = " << list_d << endl;

    //============================================================
    // Print map
    typedef pair<int, int> pair2_t;
    map<pair2_t::first_type, pair2_t::second_type> map_i_i;
    for (size_t n = 0; n < (nmax + 10); n++) {
        map_i_i.insert(pair2_t(n+1, n+2));
    }
    cout << "Map of (int, int) map_i_i = " << map_i_i << endl;

    typedef pair<int, string> pair1_t;
    map<pair1_t::first_type, pair1_t::second_type> map_i_s;
    for (size_t n = 0; n < (nmax + 10); n++) {
        map_i_s.insert(pair1_t(n+1, "one"));
    }
    cout << "Map of (int, string) map_i_s = " << map_i_s << endl;         // PROBLEM LINE 2


    return 0;
}

Related

  1. C++ print template container error (error: ambiguous overload for 'operator<<') understanding?
  2. Ambiguous overload for ‘operator<<’ in ‘std::cout <<
  3. overloading << operator for c++ stl containers
  4. Narrowing down a C++ concept to exclude certain types

Upvotes: 1

Views: 160

Answers (1)

Alan
Alan

Reputation: 1

How can I get rid of the error, without losing the generic overload, and without having to write explicit instantiations for every possible use of containers with strings? Perhaps excluding from my overload the strings

You can use std::enable_if(since you're using ) to exclude your own overload for std::string as shown below:

template <template <class... K> class Container, class ...T   >  
//added this to make use of SFINAE
std::enable_if_t<not (std::is_same_v<std::string, Container<T...>>),std::ostream&>                     operator<<(std::ostream& os, const Container<T...>& c)   
{                  
    //other code as before...

    os << "]";
    return os;
}
//other code as before...
int main(int argc, char **argv) {

     //other code as before...
     cout << "Vector of string vec_s = " << vec_s << endl; //works now    
} 

Working demo


With you could instead make use of requires and perhaps can make the code more readable. Demo

Upvotes: 3

Related Questions