Define a template operator<< for iterables

2019-07-08 00:06发布

问题:

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?

回答1:

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.