定义模板运算符<<用于可迭代项

时间:2018-11-12 17:43:25

标签: c++ templates operator-overloading c++14

我能够定义和使用:

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

但是该运算符的定义并不取决于我使用哪种容器。从那里,我想定义一个模板版本:

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

这是我的编译器生气并认真拒绝它的地方:

error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'char')

...有很多可能的候选人。

为什么不合法,如何定义这样的运算符?

1 个答案:

答案 0 :(得分:3)

正如另一个StackOverflow问题How do I fix “ambiguous overload” error when overloading operator<< (templated)? 所述,当为所有operator<<(std::ostream&, T)定义T时,您将为存在现有operator<<的类型重载它。因此,模棱两可的调用:

os << n << ", ";
        ^-- recursively calls itself? or calls the overload provided by the Standard Library?

解决方案是使用SFINAE以确保仅为可迭代类型定义重载。由于range-based for loop的定义是基于beginend的,因此我们可以使用它来区分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

现在,std::cout << data会调用您的版本,std::cout << '\n'会调用内置重载,因为无法替换Iterable = charbegin(char)未定义。