【问题标题】:Overload operator for both std::vector and std::liststd::vector 和 std::list 的重载运算符
【发布时间】:2018-12-16 06:29:54
【问题描述】:

我想使用以下代码为std::liststd::vector 重载operator<<。但这两个功能几乎相同。有没有办法将它们结合起来,,创建一个更通用的重载?

#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;
}

【问题讨论】:

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


    【解决方案1】:

    您可以使用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 问题中找到使用模板模板的其他示例。

    但是你必须小心这种结构:

    • 它仅适用于使用两个模板参数定义的容器(对于列表和向量来说可以;但不适用于集合或地图)。
    • 它可能与使用两个参数的其他模板类型发生冲突,而提取器没有专门针对这些类型。

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

    【讨论】:

      【解决方案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 代码,目前没有使用我知道的任何发布构建编译器进行编译。

      【讨论】:

      • C++20 中已经有了概念 :) 对于受限的operator&lt;&lt;,您的语法也是错误的。
      • @Rakete1111 是的,这很棒,但它是一成不变的吗?
      • 好吧,没有什么是一成不变的。是的,它发生在 C++0x 概念 IIRC 中,但我不知道在包含在标准中后删除功能的任何其他实例。所以不太可能
      • @Rakete1111 你能否更具体地说明我的语法有什么问题?
      • Range&amp;&amp; rng 其中Range 是一个无效的概念。你想要:template&lt;Range T&gt; std::ostream&amp; operator&lt;&lt;(std::ostream&amp;, T&amp;&amp; rng);,因为你的约束类型是T,而不是Range
      猜你喜欢
      • 2013-01-29
      • 2011-10-24
      • 2014-05-23
      • 1970-01-01
      • 2020-08-19
      • 1970-01-01
      • 1970-01-01
      • 2021-10-25
      相关资源
      最近更新 更多