std :: vector和std :: list

时间:2018-07-08 10:03:00

标签: c++ list templates vector operator-overloading

我想用以下代码为operator<<std::list重载std::vector。但是这两个功能几乎相同。有什么办法可以将它们组合起来,即创建更通用的重载吗?

#include <iterator>
#include <iostream>
#include <vector>
#include <list>

template <typename T>
std::ostream &operator<<(std::ostream &out, const std::vector<T> &v)
{
  if (!v.empty())
    std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
  return out;
}

template <typename T>
std::ostream &operator<<(std::ostream &out, const std::list<T> &v)
{
  if (!v.empty())
    std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
  return out;
}

int main()
{
  std::cout << std::vector<int>({1, 2, 3, 4}) << std::endl;
  std::cout << std::list<int>({1, 2, 3, 4}) << std::endl;
  return 0;
}

2 个答案:

答案 0 :(得分:4)

您可以像以下示例中那样使用template with template arguments

template <typename T, typename A, template <typename X, typename Y> class C> 
std::ostream &operator<<(std::ostream &os, const C<T,A> &container)
{
  if(!container.empty())
    std::copy(container.begin(), container.end(), std::ostream_iterator<T>(os, " "));
  return os;
}

int main() {
    list<int> l{1,2,3,4,5}; 
    vector<string> v{"one","two","three"};
    cout<<l<<endl<<v; 
    return 0;
}

在线 demo

顺便说一句,您可以在this SO问题中找到使用模板模板的其他示例。

但是您必须小心使用这种结构:

  • 它仅适用于使用两个模板参数定义的容器(对于列表和矢量而言适用,但对于集合或映射无效)。
  • 使用两个参数可能会与其他模板类型发生冲突,而这两个参数没有专门的提取器。

备注: 如果要寻找通用解决方案,则最好考虑创建一个使用迭代器作为参数的适配器模板,然后编写该适配器的通用提取器。

答案 1 :(得分:2)

输入C ++ 20,并可能包含ConceptsRanges,则可以大大简化您的问题的解决方案。

概念基本上是一个带有约束条件的模板参数,例如

// Taken from https://en.cppreference.com/w/cpp/experimental/constraints
template <typename T>
concept bool Integral = std::is_integral<T>::value;

template <Integral T> // Using concept Integral.
void foo(T i) { /* ... */ } // 'i' has to be integral, or compile time error.

现在,范围(简化)的概念符合接口(伪代码)的要求:

Range {
    begin()
    end()
}

使用它,可以编写如下内容:

template <Range T>
std::ostream& operator<<(std::ostream& out, T&& rng) {
    std::copy(std::forward<T>(rng), std::make_ostream_joiner(out, ", "));
    return out;
}

它仅适用于具有begin()end()的任何内容,例如

std::cout << std::vector{1, 2, 3, 4, 5} << std::endl; // Ok
std::cout << std::list{1, 2, 3, 4, 5} << std::endl; // Ok

此外,请注意,我使用的是std::make_ostream_joiner而不是std::ostream_iterator。它利用了一个新的C ++ 20迭代器std::ostream_joiner,该迭代器在每两个对象之间写入了分隔符,从而跳过了额外的尾部分隔符,即您将获得"1, 2, 3, 4, 5"而不是"1, 2, 3, 4, 5, "

我们希望所有这些功能都可以纳入C ++ 20中:)

注意::给出的所有示例都是假设的C ++ 20代码,目前无法使用我所知道的任何发行版构建编译器进行编译。