简体   繁体   English

C ++重载ostream <<用于任意收集

[英]C++ overloading ostream << for arbitrary collection

I'm trying to overload the << operator so I can do, for example, 我正在尝试重载<<操作符,例如,

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

Another user of this site, Chris Redford, posted some helpful code for doing this with vectors at How to print out the contents of a vector? 该站点的另一个用户Chris Redford在如何打印矢量的内容上发布了一些有用的矢量代码。 . I've attempted to adapt his code to work with other types of collection as follows: 我试图修改他的代码以使其与其他类型的集合一起使用,如下所示:

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

Obviously I'm a noob when it comes to writing templates, so any tips for reading materials would be welcome. 显然,在编写模板方面我是一个菜鸟,因此欢迎阅读材料的任何提示。 Hopefully it is clear that I'd like this to work for anything that can do .begin() and .end(). 希望很明显,我希望此方法可用于可以执行.begin()和.end()的任何事情。 When compiling this with 当用

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

, I get a compiler error saying "ambiguous overload for 'operator<<'" and a bunch of gobbledy goop I don't understand. ,我收到一个编译器错误,上面写着“'operator <<'的模棱两可的重载”和一堆我听不懂的笨拙的粘糊糊。 I think gcc might be trying to redefine what << means for std::string, but I'm not sure. 我认为gcc可能正在尝试重新定义<<对于std :: string的含义,但是我不确定。 Ideally, I'd like to tell the compiler not to try to redefine this operation for types for which it is already defined. 理想情况下,我想告诉编译器不要尝试针对已经为其定义的类型重新定义此操作。 I'm also using -std=C++14 so I'm open to clever use of auto. 我也使用-std = C ++ 14,所以我愿意聪明地使用auto。 Any suggestions? 有什么建议么?

Edit: corrected bad use of T... in original question. 编辑:更正了T ...在原始问题中的错误使用。

Just found following: Pretty-print C++ STL containers 刚刚发现以下内容: 漂亮打印的C ++ STL容器

Solution looks quite sophisticated. 解决方案看起来相当复杂。 If you want to go for some more simple solution you could do the following: 如果您想寻求更简单的解决方案,则可以执行以下操作:

Writing a template operator<< is very likely to conflict with any exisiting declaration of operator<< . 编写operator<<的模板很可能与任何现有的operator<<声明相冲突。 What you could do is use a print function as already proposed and write some smaller wrappers, eg: 您可以做的是使用已经建议的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);
}

// ...

I've made something like that for a another project, but I had to make an "<<" for each type and not all types are implemented here. 我为另一个项目做了类似的事情,但是我必须为每种类型都做一个“ <<”,而且并不是所有的类型都在这里实现。

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

There's a problem with your function: 您的函数存在问题:

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

That will try to print any type which is a specialization of a template! 那将尝试打印任何是模板特化的类型! So it will be used for std::pair<int, double> and other non-containers, and will fail to compile because they don't have begin() and end() member functions. 因此它将用于std::pair<int, double>和其他非容器,并且由于它们没有begin()end()成员函数而无法编译。

The solution is to constrain the template so it only matches types that can be iterated over. 解决方案是限制模板,使其仅匹配可以迭代的类型。 This is based on my code in <redi/printers.h> and will print anything that can be passed to std::begin : 这基于我在<redi/printers.h>代码,并将打印可以传递给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;
    }

You still have the problem that this function will match for types which already have their own overloaded operator<< and so will either do the wrong thing or will be ambiguous (that is a problem for std::string , for example). 您仍然有一个问题,该函数将与已经有自己重载的operator<<类型匹配,因此将执行错误的操作或导致模棱两可(例如,这是std::string的问题)。

In <redi/printers.h> I solve that by defining a different function to do the printing, called print_one , and the overload that prints ranges is disabled if the type can already be printed using operator<< . <redi/printers.h>我通过定义一个不同的函数进行打印来解决该问题,称为print_one ,并且如果可以使用operator<<打印该类型,则禁用打印范围的重载。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM