【问题标题】:C++ overloading ostream << for arbitrary collectionC++ 重载 ostream << 用于任意集合
【发布时间】:2015-10-13 18:16:57
【问题描述】:

我正在尝试重载

list<string> string_list = ...;
vector<double> double_vector = ...;
set<list<int>> int_list_set = ...;
cout << string_list << double_vector << int_list_set << endl;

该站点的另一位用户 Chris Redford 在 How to print out the contents of a vector? 上发布了一些有用的代码,用于使用向量执行此操作。我试图调整他的代码以与其他类型的集合一起使用,如下所示:

template <template <typename...> class collection, typename T>
std::ostream& operator<<(std::ostream& out, const collection<T>& c)  {
  out << "[ ";
  out << *c.begin();
  for(auto it=next(c.begin(),1); it!=c.end(); ++it) {
    out << " , ";
    out << *it;
  }
  out << " ]";
  return out;
}

显然,在编写模板方面我是个菜鸟,因此欢迎任何有关阅读材料的提示。希望很明显,我希望它适用于任何可以做 .begin() 和 .end() 的事情。使用

编译时
int main(int argc, char **argv) {
  list<string> words;
  words.push_back("hello");
  words.push_back("world");
  cout << words << endl;
}

,我收到一个编译器错误,提示“'operator

编辑:更正了原始问题中对 T... 的错误使用。

【问题讨论】:

  • 为什么要使用模板参数包,即 typename...?我看不出有必要。
  • 我的建议:不要这样做。该模板不是特定的并且可能是有害的(防止专用容器的过载选择)。但这只是一个猜测。如果您将其用于调试,请使用宏。
  • @user2672165 正如我所说,我是模板新手,但我认为如果我想让它适用于泛型的泛型,比如 list>,这种事情是必要的。
  • @DieterLücking 那么您的建议是拥有一个包含所有不同容器的 Chris Redford 代码版本吗?我认为模板的目的是避免这样的事情。另外,由于模板调用了 .begin() 和 .end(),看起来这不会对任何没有的东西有害,对吗?
  • 代替重载

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


【解决方案1】:

刚刚发现以下内容: Pretty-print C++ STL containers

解决方案看起来相当复杂。如果您想寻求一些更简单的解决方案,您可以执行以下操作:

编写模板operator&lt;&lt; 很可能与operator&lt;&lt; 的任何现有声明发生冲突。 您可以做的是使用已经提出的print 函数并编写一些较小的包装器,例如:

template <class collection>
std::ostream& printCollection (std::ostream& out, const collection& c)  {
  out << "[ ";
  out << *c.begin();

  for(auto it = next(c.begin(), 1); it != c.end(); ++it) {
    out << " , ";
    out << *it;
  }
  out << " ]";
  return out;
}

template <typename T>
std::ostream& operator<< (std::ostream& os, std::list<T>& collection) {
  return printCollection(os, collection);
}

// ...

【讨论】:

    【解决方案2】:

    我已经为另一个项目制作了类似的东西,但我必须为每种类型制作一个“

    #include <iostream>
    #include <vector>
    #include <list>
    #include <utility>
    #include <algorithm>
    #include <string>
    #include <array>
    #include <set>
    #include <map>
    #include <assert.h>
    
    using namespace std;
    
    int indentCnt = 0;
    static string indents = "  ";
    class AddOne {
        int& counter;
    public:
        AddOne(int& pre = indentCnt) : counter(pre) {
            counter++;
            if (indents.length()<2*counter)
                indents += indents;
         }
        ~AddOne() {
            counter--;
        }
    };
    
    string indent() {
        assert(indents.length() >= 2*indentCnt);
    
        return indents.substr(0, 2*indentCnt);
    }
    
    enum delimiters { deBefore, deBetween, deAfter, deNum };
    using delims = array<string, deNum>;
    
    template<typename cType>
    std::ostream& forallout(std::ostream& out, const cType& v, const delims& delim) {
        auto it = v.begin();
        out << delim[deBefore];
        if (it != v.end()) {
            for (; it != prev(v.end()); it++) // to avoid the seperator after last.
                out << *it << delim[deBetween];
            out << *it;
        } else
            out << "~Empty~";
        out << delim[deAfter];
    
        return out;
    }
    
    template <typename kType, typename dType>
    std::ostream& operator<<(std::ostream& out, const std::map<kType, dType>& c)  {
        delims de { indent()+"[\n  "+indent(), ",\n  "+indent(), "\n"+indent()+"]" };
        AddOne aMap(indentCnt);
    
        return forallout(out, c, de);
    }
    
    template <typename dType>
    std::ostream& operator<<(std::ostream& out, const std::vector<dType>& c)  {
        delims de { indent()+"[\n", ",\n", "\n"+indent()+"]" };
        AddOne aVec(indentCnt);
    
        return forallout(out, c, de);
    }
    
    template <typename dType>
    std::ostream& operator<<(std::ostream& out, const std::list<dType>& c)  {
        delims de { indent()+"(", "<->", ")" };
    
        return forallout(out, c, de);
    }
    
    template <typename dType>
    std::ostream& operator<<(std::ostream& out, const std::set<dType>& c) {
        delims de { indent()+"{", ", ", "}" };
    
        return forallout(out, c, de);
    }
    
    template <typename dType, typename kType>
    std::ostream& operator<<(std::ostream& out, const std::pair<kType, dType>& c)  {
        delims de { "[", ":", "]" };
    
        out << de[deBefore] << c.first << de[deBetween] << c.second << de[deAfter];
    
        return out;
    }
    
    template <typename kType>
    std::ostream& operator<<(std::ostream& out, const std::pair<kType, string>& c)  {
        delims de { "[", ":", "]" };
    
        out << de[deBefore] << c.first << de[deBetween] << "\"" << c.second << "\"" << de[deAfter];
    
        return out;
    }
    

    【讨论】:

      【解决方案3】:

      你的函数有问题:

      template <template <typename...> class collection, typename T>
      std::ostream& operator<<(std::ostream& out, const collection<T>& c)  {
      

      这将尝试打印模板的特化 any 类型!所以它将用于std::pair&lt;int, double&gt;和其他非容器,并且由于它们没有begin()end()成员函数而无法编译。

      解决方案是限制模板,使其仅匹配可迭代的类型。这是基于我在&lt;redi/printers.h&gt; 中的代码,并将打印可以传递给std::begin 的任何内容:

        template<typename Range,
                 typename = decltype(*std::begin(std::declval<Range>()))>
          std::ostream&
          operator<<(std::ostream& os, const Range& range)
          {
            os << '[';
            const char* sep = "";
            for (auto& e : range)
            {
              os << sep << e;
              sep = ", ";
            }
            os << ']';
            return os;
          }
      

      您仍然会遇到问题,即此函数将匹配已经具有自己重载的 operator&lt;&lt; 的类型,因此要么会做错事,要么会模棱两可(例如,std::string 的问题)。

      &lt;redi/printers.h&gt; 中,我通过定义一个不同的函数来解决这个问题,称为print_one,如果已经可以使用operator&lt;&lt; 打印类型,则禁用打印范围的重载。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-25
        • 1970-01-01
        • 2016-02-19
        • 1970-01-01
        • 2020-02-10
        • 1970-01-01
        • 1970-01-01
        • 2015-10-09
        相关资源
        最近更新 更多