我能否重载插入运算符以使用模板化的STL容器?

5
我有一个关于STL容器的例子,所以我正在阅读相关内容。对我来说,反复使用for循环打印容器内容变得非常乏味。因此,我考虑重载插入运算符<<,这样我就可以写成:std::cout << container << std::endl;
template<class T>
std::ostream& operator<<(std::ostream& out, const std::list<T>& v) {
    for (const auto& e : v)
        out << e << ", ";
    return out;
}

int main() {

    std::list<int> vi{ 10, 24, 81, 57, 2019 };

    vi.pop_back();
    std::cout << vi << std::endl; // 10, 24, 81, 57,

    std::deque<std::string> names{ "Hello", "STL Containers" };
    std::cout << names << std::endl; // error here. Bacause I've not overloaded << to take a std::deque<T>


}

如上所述,我觉得打印某种类型的list非常方便。但问题是我只能打印列表,而不能打印其他类型的容器,比如vectordeque...

那么如何重载<<来接受T<U>类型的容器呢?或者说我应该为所有容器专门定义吗?在真实的例子中,我不应该这样做吗?


2
这是您的 mcve - πάντα ῥεῖ
1
简短回答:你不应该这样做。一些标准容器(也就是满足容器要求的类型)已经拥有插入运算符。例如,std::string - 为所有容器生成插入运算符会导致可诊断错误,因为每当您尝试输出这些容器时都存在两个同样可行的插入运算符,并且没有理由优先选择一个而非另一个。 - Peter
为什么你输出标准容器这么频繁以至于变得“乏味”?这是一个奇怪的程序!也许你的程序没有完全做到它应该做的事情。 - Lightness Races in Orbit
2个回答

1
您可以提供以下重载:
template <class...>
using void_t = void;

template <class... Args>
constexpr bool exists_t_v(){
  return std::is_same<void_t<Args...>, void>::value;
}


template <class Container, class = std::enable_if<
  exists_t_v<
    decltype(std::begin(std::declval<Container>())),
    decltype(std::end(std::declval<Container>()))
  >()
>>
std::ostream& operator<<(std::ostream& os, Container&& container){
  for(const auto& e : std::forward<Container>(container))
    os << e << ',';

  return os;
}

这将使任何类型为Container且定义了std::begin(container)std::end(container)container实例使用该重载。
这些解决方案中的一个问题是对std::string支持,以及任何已经提供自己重载的类型,其std::begin()std::end()已被定义。

0

我希望这是不可能的,我尝试将迭代器放入operator<<中,但它们甚至需要具有精确的迭代器类型,这是使用ostream_iterator的最接近的可能解决方案:

template<class T2, class T>
void showT(ostream&out, T b, T end){
    out << '[';
    copy(b, end, ostream_iterator<T2>{out, ", "});
    out<<"]";
    return;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接