如何将 std::vector
的内容打印到屏幕上?
实现以下 operator<<
的解决方案也很好:
template<container C, class T, String delim = ", ", String open = "[", String close = "]">
std::ostream & operator<<(std::ostream & o, const C<T> & x)
{
// ... What can I write here?
}
这是我到目前为止所拥有的,没有单独的功能:
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>
#include <sstream>
#include <cstdio>
using namespace std;
int main()
{
ifstream file("maze.txt");
if (file) {
vector<char> vec(istreambuf_iterator<char>(file), (istreambuf_iterator<char>()));
vector<char> path;
int x = 17;
char entrance = vec.at(16);
char firstsquare = vec.at(x);
if (entrance == 'S') {
path.push_back(entrance);
}
for (x = 17; isalpha(firstsquare); x++) {
path.push_back(firstsquare);
}
for (int i = 0; i < path.size(); i++) {
cout << path[i] << " ";
}
cout << endl;
return 0;
}
}
原文由 forthewinwin 发布,翻译遵循 CC BY-SA 4.0 许可协议
如果您有 C++11 编译器,我建议使用基于范围的 for 循环(见下文);或者使用迭代器。但是你有几个选择,我将在下面解释所有这些。
基于范围的 for 循环 (C++11)
在 C++11(及更高版本)中,您可以使用新的基于范围的 for 循环,如下所示:
for循环语句中的类型
char
应该是向量元素的类型path
而不是整数索引类型。换句话说,由于path
是std::vector<char>
类型,因此应该出现在基于范围的 for 循环中的类型是char
。但是,您可能经常会看到显式类型替换为auto
占位符类型:无论您使用显式类型还是
auto
关键字,对象i
的值都是path
对象中实际项目的副本。因此,循环中对i
的所有更改都不会保留在path
本身中:如果您想禁止在 for 循环中更改
i
的复制值,您可以强制i
的类型为const char
像这样:如果您想修改
path
中的项目,以便这些更改在path
在 for 循环之外持续存在,那么您可以使用如下引用:即使您不想修改
path
,如果复制对象的成本很高,您应该使用 const 引用而不是按值复制:迭代器
在 C++11 之前,规范的解决方案是使用迭代器,这仍然是完全可以接受的。它们的用法如下:
如果要在 for 循环中修改向量的内容,请使用
iterator
而不是const_iterator
。补充:typedef/类型别名(C++11)/auto(C++11)
这不是另一种解决方案,而是对上述
iterator
解决方案的补充。如果您使用的是 C++11 标准(或更高版本),那么您可以使用auto
关键字来提高可读性:这里
i
的类型将是非常量的(即编译器将使用std::vector<char>::iterator
作为i
的类型)。这是因为我们调用了begin
方法,因此编译器从中推断出i
的类型。如果我们调用cbegin
方法(“c”代表 const),那么i
将是std::vector<char>::const_iterator
:如果您对编译器推导类型不满意,那么在 C++11 中,您可以使用类型别名来避免必须一直输入向量(养成的好习惯):
如果您无法访问 C++11 编译器(或者由于某种原因不喜欢类型别名语法),那么您可以使用更传统的
typedef
:边注:
在这一点上,您以前可能遇到过迭代器,也可能没有,您可能听说过或可能没有听说过迭代器是您“应该”使用的,并且可能想知道为什么。答案并不容易理解,但简而言之,这个想法是迭代器是一种抽象,可以保护您免受操作细节的影响。
拥有一个执行您想要的操作(如顺序访问)的对象(迭代器)而不是您自己编写详细信息(“详细信息”是实际访问向量元素的代码)是很方便的。您应该注意到,在 for 循环中,您只要求迭代器返回一个值(
*i
,其中i
是迭代器) - 您永远不会与path
直接自己。逻辑是这样的:你创建一个迭代器并给它你想要循环的对象(iterator i = path.begin()
),然后你所做的就是让迭代器为你获取下一个值(*i
);您永远不必担心迭代器是如何做到这一点的——这是它的业务,而不是您的业务。好的,但有什么意义呢?好吧,想象一下,如果获得价值并不简单。如果它涉及一些工作怎么办?你不必担心,因为迭代器已经为你处理了——它整理了细节,你需要做的就是向它询问一个值。此外,如果您将容器从
std::vector
更改为其他内容怎么办?从理论上讲,即使访问新容器中的元素的细节发生了变化,您的代码也不会改变:请记住,迭代器会在幕后为您整理所有细节,因此您根本不需要更改代码-- 你只需向迭代器询问容器中的下一个值,和以前一样。因此,虽然这看起来像是对循环向量的混淆过度杀伤,但迭代器的概念背后有充分的理由,因此您不妨习惯使用它们。
索引
您还可以使用整数类型在 for 循环中显式地索引向量的元素:
如果您打算这样做,最好使用容器的成员类型,如果它们可用且合适的话。
std::vector
有一个名为size_type
的成员类型用于此作业:它是由size
方法返回的类型。为什么不优先使用
iterator
解决方案?对于简单的情况,您可以这样做,但使用iterator
会带来几个优势,我在上面已经简要概述了这些优势。因此,我的建议是避免这种方法,除非你有充分的理由。标准::复制 (C++11)
见 约书亚的回答。您可以使用 STL 算法
std::copy
将向量内容复制到输出流中。我没有什么要补充的,只是说我不使用这种方法;但除了习惯之外,没有什么好的理由。std::ranges::copy (C++20)
为了完整起见,C++20 引入了范围,它可以作用于
std::vector
的整个范围,因此不需要begin
和end
:除非您有最近的编译器(显然 在 GCC 上至少版本 10.1 ),否则即使您可能有一些可用的 C++20 功能,您也可能不会支持范围。
重载 std::ostream::operator<<
另请参阅 下面 Chris 的回答。这更像是对其他答案的补充,因为您仍然需要在重载中实现上述解决方案之一,但好处是代码更简洁。这就是您可以使用
std::ranges::copy
解决方案的方式:现在您可以像基本类型一样将您的
Path
对象传递给您的输出流。使用上述任何其他解决方案也应该同样简单。结论
此处介绍的任何解决方案都可以使用。哪个是“最好的”取决于您(以及上下文或您的编码标准)。任何比这更详细的东西可能最好留给另一个可以正确评估优点/缺点的问题,但一如既往,用户偏好总是会发挥作用:所提出的解决方案都不是客观错误的,但有些解决方案对每个编码人员来说看起来更好.
附录
这是我发布的早期解决方案的扩展解决方案。由于该帖子一直受到关注,因此我决定对其进行扩展并参考此处发布的其他出色解决方案,至少是那些我过去至少亲自使用过一次的解决方案。但是,我鼓励读者查看下面的答案,因为可能有一些我已经忘记或不知道的好建议。