C++重载 ostream <<以进行任意收集
C++ overloading ostream << for arbitrary collection
我试图重载<<操作符,例如,
list<string> string_list = ...;
vector<double> double_vector = ...;
set<list<int>> int_list_set = ...;
cout << string_list << double_vector << int_list_set << endl;
这个网站的另一个用户,克里斯·雷德福,张贴了一些有用的代码来做这个与矢量在如何打印出矢量的内容?. 我试图修改他的代码,使其与其他类型的集合一起工作,如下所示:
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<<'有歧义的重载"和一堆我不理解的废话。我认为gcc可能试图重新定义什么<<表示std::string,但我不确定。理想情况下,我想告诉编译器不要尝试为已经定义了该操作的类型重新定义该操作。我还使用了-std= c++ 14,所以我可以聪明地使用auto。有什么建议吗?
编辑:纠正错误使用T…
刚刚发现以下内容:c++ STL容器
解决方案看起来相当复杂。如果你想要一些更简单的解决方案,你可以这样做:
编写模板operator<<
很可能与任何现有的operator<<
声明相冲突。您可以做的是使用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);
}
// ...
我在另一个项目中也做过类似的东西,但是我必须为每个类型都做一个"<<",这里并不是所有的类型都实现了。
#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;
}
你的函数有问题:
template <template <typename...> class collection, typename T>
std::ostream& operator<<(std::ostream& out, const collection<T>& c) {
将尝试打印任何类型,这是一个模板的专门化!因此,它将用于std::pair<int, double>
和其他非容器,并且将无法编译,因为它们没有begin()
和end()
成员函数。
解决方案是约束模板,使其只匹配可以迭代的类型。这是基于我在<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;
}
你仍然有一个问题,这个函数将匹配已经有自己的重载operator<<
的类型,因此要么会做错误的事情,要么会模棱两可(这是std::string
的问题,例如)。
在<redi/printers.h>
中,我通过定义一个不同的函数来执行打印来解决这个问题,称为print_one
,如果已经可以使用operator<<
打印类型,则禁用打印范围的过载。
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- 如何防止clang格式在流运算符调用之间添加换行符<<
- <<操作员在下面的行中工作
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 从函数返回任意简单类型的数据
- 如何生成一个随机的 n 位数,其中 n 是任意的
- C++ - 声明指向返回任何类型并获取任意数量参数的函数的指针
- 收银美分计算不起作用?
- 将正态随机变量与任意 RHO(corrcoef) 相关联
- 在C++中生成任意嵌套的向量
- 任意大小的 constexpr 数组是否可以用作 switch 语句中的案例?
- FlatBuffers/Protobuf 中是否有支持任意 24 位有符号整数定义的可移植二进制序列化架构?
- OpenGL - 在任意轴上平移对象
- 如何绕任意轴旋转点?
- 在 std::vector<无符号字符中存储任意数据的方法>
- 如何在VS2019中获取数组的任意对齐?
- 具有任意数量参数的模板函数
- 从加密项目向量中解密任意选择的元素会导致无效的 PKCS #7 块错误
- 如何在C++中实现节点数任意的通用树数据结构?