Error on successive enable_if overload call

Viewed 58

Basically, the idea is to overload operator << for anything iterable, like vectors, lists, and custom classes properly defining begin() and the iterator scheme.

Initially, I wrote the following prototype

template<template<class, class ...> class Container, class T, class ... Whatever>
std::ostream& operator<<(std::ostream& out, const Container<T, Whatever...>& container) { stuff }

The problem is that I obviously get clashes with anything matching the template definition and already overloading <<, such as std::string. So, if I write cout << string { "Hello" }, it is ambiguous. I understand why.

So, the idea is to activate the above overload if and only if an operator<< isn't already defined. I chose to discard this case with std::enable_if in the following way:

template<class> struct sfinae_true : std::true_type {};
template<class ToPrint> static auto test_insertion(int) -> sfinae_true<decltype(std::cout << std::declval<ToPrint>())>;
template<class ToPrint> static auto test_insertion(long) -> std::false_type;
template<class ToPrint> struct is_printable : decltype(test_insertion<ToPrint>(0)) {};
template<class ToPrint> using NotPrintable = std::enable_if_t<! is_printable<ToPrint>::value>;

//overload not availlable if operator<< is already defined (avoids ambiguity)
template<template<class, class ...> class Container, class T, class ... Whatever, NotPrintable<Container<T, Whatever...>>* = nullptr>
std::ostream& operator<<(std::ostream& out, const Container<T, Whatever...>& container)
{
    //stuff
}

The problem is, somehow, in C++17 at least, I can't print twice. That is,

vector<int> v1 = {1,2,3,4,5};    
vector<int> v2 = {6,7,8};
cout << v1 << endl;
cout << v2 << endl;

the compiler tells me there is no operator<< for v2... Incredible right ? I have no idea why, and consequently how to fix the problem.

Remark: I know, I could simplyfy the prototype to just template<class Container> std::ostream& operator<<(std::ostrea& out, const Container& c), but let's say "I don't want to".

Complete sample code to copy / paste:

#include <iostream>
#include <type_traits>
#include <vector>
#include <string>

template<class> struct sfinae_true : std::true_type {};
template<class ToPrint> static auto test_insertion(int) -> sfinae_true<decltype(std::cout << std::declval<ToPrint>())>;
template<class ToPrint> static auto test_insertion(long) -> std::false_type;
template<class ToPrint> struct is_printable : decltype(test_insertion<ToPrint>(0)) {};
template<class ToPrint> using NotPrintable = std::enable_if_t<! is_printable<ToPrint>::value>;

//overload not availlable if operator<< is already defined (avoids ambiguity)
template<template<class, class ...> class Container, class T, class ... Whatever, NotPrintable<Container<T, Whatever...>>* = nullptr>
std::ostream& operator<<(std::ostream& out, const Container<T, Whatever...>& container)
{
    out << "{ ";

    auto it = container.begin();
    auto it_end = container.end();

    if(it != it_end)
    {
        out << *it;
        ++it;
    }

    for(; it != it_end; ++it)
        out << " , " << (*it);

    out << " }";

    return out;
}

using namespace std;

int main()
{
    vector<int> v1 = {1,2,3,4,5};    
    vector<int> v2;       

    cout << v1 << endl; //prints {1, 2, 3? 4, 5}
    cout << v2 << endl; //compile error : no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘std::vector<int>’)
    
    cout << string {"Hello"} << endl; //works fine
}

The error message is

error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘std::vector<int>’)
      cout << v2 << endl;

I'm compiling with g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0, with the command g++ -o sample sample.cpp, where sample.cpp is the file containing the above code.

1 Answers

Alright, so basically, my compiler version was just too old. On gcc 9.3 it works just as intended.

Topic closed. (I read that I can't close my own topic if I don't have enough rep, which I don't...)

Related