如何打印出向量的内容? [英] How do I print out the contents of a vector?

查看:45
本文介绍了如何打印出向量的内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何将 std::vector 的内容打印到屏幕上?

How do I print out the contents of a std::vector to the screen?

实现以下 operator<< 的解决方案也不错:

A solution that implements the following operator<< would be nice as well:

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


这是我目前所拥有的,没有单独的函数:


Here is what I have so far, without a separate function:

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

推荐答案

如果您有 C++11 编译器,我建议您使用基于范围的 for 循环(见下文);或者使用迭代器.但是你有几个选项,我将在下面解释所有这些.

If you have a C++11 compiler, I would suggest using a range-based for-loop (see below); or else use an iterator. But you have several options, all of which I will explain in what follows.

在 C++11(及更高版本)中,您可以使用新的基于范围的 for 循环,如下所示:

In C++11 (and later) you can use the new range-based for-loop, which looks like this:

std::vector<char> path;
// ...
for (char i: path)
    std::cout << i << ' ';

for-loop 语句中的类型 char 应该是向量 path 元素的类型,而不是整数索引类型.换句话说,由于 pathstd::vector 类型,所以应该出现在基于范围的 for 循环中的类型是 char.但是,您可能会经常看到显式类型被替换为 auto 占位符类型:

The type char in the for-loop statement should be the type of the elements of the vector path and not an integer indexing type. In other words, since path is of type std::vector<char>, the type that should appear in the range-based for-loop is char. However, you will likely often see the explicit type replaced with the auto placeholder type:

for (auto i: path)
    std::cout << i << ' ';

无论您使用显式类型还是 auto 关键字,对象 i 的值都是 路径中实际项目的副本 对象.因此,循环中对 i 的所有更改都不会保留在 path 本身中:

Regardless of whether you use the explicit type or the auto keyword, the object i has a value that is a copy of the actual item in the path object. Thus, all changes to i in the loop are not preserved in path itself:

std::vector<char> path{'a', 'b', 'c'};

for (auto i: path) {
    i = '_'; // 'i' is a copy of the element in 'path', so although
             // we can change 'i' here perfectly fine, the elements
             // of 'path' have not changed
    std::cout << i << ' '; // will print: "_ _ _"
}

for (auto i: path) {
    std::cout << i << ' '; // will print: "a b c"
}

如果您也希望禁止在 for 循环中更改 i 的复制值,您可以强制 i 的类型为 const char 像这样:

If you would like to proscribe being able to change this copied value of i in the for-loop as well, you can force the type of i to be const char like this:

for (const auto i: path) {
    i = '_'; // this will now produce a compiler error
    std::cout << i << ' ';
}

如果您想修改 path 中的项目,以便这些更改在 for 循环之外的 path 中持续存在,那么您可以使用如下引用:

If you would like to modify the items in path so that those changes persist in path outside of the for-loop, then you can use a reference like so:

for (auto& i: path) {
    i = '_'; // changes to 'i' will now also change the
             // element in 'path' itself to that value
    std::cout << i << ' ';
}

即使你不想修改path,如果对象的复制开销很大,你应该使用const引用而不是按值复制:

and even if you don't want to modify path, if the copying of objects is expensive you should use a const reference instead of copying by value:

for (const auto& i: path)
    std::cout << i << ' ';

迭代器

在 C++11 之前,规范的解决方案是使用迭代器,这仍然是完全可以接受的.它们的用法如下:

iterators

Before C++11 the canonical solution would have been to use an iterator, and that is still perfectly acceptable. They are used as follows:

std::vector<char> path;
// ...
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

如果要在 for 循环中修改向量的内容,请使用 iterator 而不是 const_iterator.

If you want to modify the vector's contents in the for-loop, then use iterator rather than const_iterator.

这不是另一种方案,而是对上述iterator方案的补充.如果您使用的是 C++11 标准(或更高版本),那么您可以使用 auto 关键字来帮助提高可读性:

This is not another solution, but a supplement to the above iterator solution. If you are using the C++11 standard (or later), then you can use the auto keyword to help the readability:

for (auto i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

这里 i 的类型将是非常量(即编译器将使用 std::vector::iterator 作为 的类型>i).这是因为我们调用了 begin 方法,所以编译器从中推断出 i 的类型.如果我们改为调用 cbegin 方法(c"代表 const),那么 i 将是一个 std::vector::const_iterator:

Here the type of i will be non-const (i.e., the compiler will use std::vector<char>::iterator as the type of i). This is because we called the begin method, so the compiler deduced the type for i from that. If we call the cbegin method instead ("c" for const), then i will be a std::vector<char>::const_iterator:

for (auto i = path.cbegin(); i != path.cend(); ++i) {
    *i = '_'; // will produce a compiler error
    std::cout << *i << ' ';
}

如果您对编译器推导类型不满意,那么在 C++11 中,您可以使用类型别名来避免一直输入向量(养成一个好习惯):

If you're not comfortable with the compiler deducing types, then in C++11 you can use a type alias to avoid having to type the vector out all the time (a good habit to get into):

using Path = std::vector<char>; // C++11 onwards only
Path path; // 'Path' is an alias for std::vector<char>
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

如果您无权使用 C++11 编译器(或者由于某种原因不喜欢类型别名语法),那么您可以使用更传统的 typedef:

If you do not have access to a C++11 compiler (or don't like the type alias syntax for whatever reason), then you can use the more traditional typedef:

typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
Path path;
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

附注:

在这一点上,您之前可能遇到也可能没有遇到过迭代器,并且您可能听说过也可能没有听说过迭代器就是您所假设"的.使用,可能想知道为什么.答案并不容易理解,但简而言之,其思想是迭代器是一种抽象,可以使您免受操作细节的影响.

side note:

At this point, you may or may not have come across iterators before, and you may or may not have heard that iterators are what you are "supposed" to use, and may be wondering why. The answer is not easy to appreciate, but, in brief, the idea is that iterators are an abstraction that shield you from the details of the operation.

拥有一个对象(迭代器)来执行您想要的操作(如顺序访问)而不是您自己编写详细信息(详细信息"是执行对元素的实际访问的代码)很方便向量).您应该注意到,在 for 循环中,您只要求迭代器返回一个值(*i,其中 i 是迭代器)——您永远不会与 path 直接交互.逻辑是这样的:你创建一个迭代器并给它你想要循环的对象(iterator i = path.begin()),然后你所做的就是让迭代器得到你的下一个值 (*i);您永远不必担心迭代器究竟是如何做到的——那是它的事,而不是您的事.

It is convenient to have an object (the iterator) that does the operation you want (like sequential access) rather than you writing the details yourself (the "details" being the code that does the actual accessing of the elements of the vector). You should notice that in the for-loop you are only ever asking the iterator to return you a value (*i, where i is the iterator) -- you are never interacting with path directly itself. The logic goes like this: you create an iterator and give it the object you want to loop over (iterator i = path.begin()), and then all you do is ask the iterator to get the next value for you (*i); you never had to worry exactly how the iterator did that -- that's its business, not yours.

好的,但有什么意义呢?好吧,想象一下,如果获得一个值并不简单.如果它涉及一些工作怎么办?你不必担心,因为迭代器已经为你处理了——它整理了细节,你需要做的就是向它询问一个值.此外,如果您将容器从 std::vector 更改为其他内容会怎样?理论上,即使访问新容器中元素的细节发生变化,您的代码也不会改变:请记住,迭代器会在幕后为您整理出所有细节,因此您根本不需要更改代码-- 您只需向迭代器询问容器中的下一个值,与之前相同.

OK, but what's the point? Well, imagine if getting a value wasn't simple. What if it involves a bit of work? You don't need to worry, because the iterator has handled that for you -- it sorts out the details, all you need to do is ask it for a value. Additionally, what if you change the container from std::vector to something else? In theory, your code doesn't change even if the details of how accessing elements in the new container do: remember, the iterator sorts all the details out for you behind the scenes, so you don't need to change your code at all -- you just ask the iterator for the next value in the container, same as before.

因此,虽然这对于遍历向量似乎有点令人困惑,但迭代器的概念背后有充分的理由,因此您最好习惯使用它们.

So, whilst this may seem like confusing overkill for looping through a vector, there are good reasons behind the concept of iterators and so you might as well get used to using them.

您还可以使用整数类型明确索引 for 循环中向量的元素:

You can also use a integer type to index through the elements of the vector in the for-loop explicitly:

for (int i=0; i<path.size(); ++i)
    std::cout << path[i] << ' ';

如果您打算这样做,最好使用容器的成员类型(如果它们可用且合适).std::vector 有一个名为 size_type 的成员类型用于此作业:它是 size 方法返回的类型.

If you are going to do this, it's better to use the container's member types, if they are available and appropriate. std::vector has a member type called size_type for this job: it is the type returned by the size method.

typedef std::vector<char> Path; // 'Path' now a synonym for std::vector<char>
for (Path::size_type i=0; i<path.size(); ++i)
    std::cout << path[i] << ' ';

为什么不优先使用 iterator 解决方案?对于简单的情况,您可以这样做,但是使用 iterator 会带来几个优点,我在上面简要概述了这些优点.因此,我的建议是避免使用这种方法,除非您有充分的理由.

Why not use this in preference to the iterator solution? For simple cases, you can do that, but using an iterator brings several advantages, which I have briefly outlined above. As such, my advice would be to avoid this method unless you have good reasons for it.

请参阅约书亚的回答.您可以使用 STL 算法 std::copy 将矢量内容复制到输出流上.我没有什么要补充的,只是说我不使用这种方法;但除了习惯之外,没有什么好的理由.

See Joshua's answer. You can use the STL algorithm std::copy to copy the vector contents onto the output stream. I don't have anything to add, except to say that I don't use this method; but there's no good reason for that besides habit.

为了完整性,C++20 引入了范围,它可以作用于 std::vector 的整个范围,所以不需要 begin结束:

For completeness, C++20 introduced ranges, which can act on the whole range of a std::vector, so no need for begin and end:

#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

std::vector<char> path;
// ...
std::ranges::copy(path, std::ostream_iterator<char>(std::cout, " "));

除非你有最近的编译器(显然在 GCC 上 至少版本 10.1),即使您可能有一些 C++20 功能可用,您也可能不会有范围支持.

Unless you have a recent compiler (on GCC apparently at least version 10.1), likely you will not have ranges support even if you might have some C++20 features available.

另请参阅下面克里斯的回答.这更像是对其他答案的补充,因为您仍然需要在重载中实现上述解决方案之一,但好处是代码更简洁.这是您如何使用上面的 std::ranges::copy 解决方案:

See also Chris's answer below. This is more a complement to the other answers since you will still need to implement one of the solutions above in the overloading, but the benefit is much cleaner code. This is how you could use the std::ranges::copy solution above:

#include <iostream>
#include <vector>
#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

using Path = std::vector<char>; // type alias for std::vector<char>

std::ostream& operator<< (std::ostream& out, const Path& v) {
    if ( !v.empty() ) {
        out << '[';
        std::ranges::copy(v, std::ostream_iterator<char>(out, ", "));
        out << "]"; // use two ANSI backspace characters '' to overwrite final ", "
    }
    return out;
}

int main() {
    Path path{'/', 'f', 'o', 'o'};

    // will output: "path: [/, f, o, o]"
    std::cout << "path: " << path << std::endl;

    return 0;
}

现在您可以像基本类型一样将 Path 对象传递到输出流.使用上述任何其他解决方案也应该同样简单.

Now you can pass your Path objects to your output stream just like fundamental types. Using any of the other solutions above should also be equally straightforward.

此处介绍的任何解决方案都可以使用.哪个是最好的"取决于您(以及上下文或您的编码标准).任何比这更详细的内容可能最好留给另一个可以正确评估利弊的问题,但与往常一样,用户偏好将始终发挥作用:所提供的解决方案都没有客观上是错误的,但有些解决方案对每个编码员来说看起来更好.

Any of the solutions presented here will work. It's up to you (and context or your coding standards) on which one is the "best". Anything more detailed than this is probably best left for another question where the pros/cons can be properly evaluated, but as always user preference will always play a part: none of the solutions presented are objectively wrong, but some will look nicer to each coder.

这是我较早发布的解决方案的扩展解决方案.由于该帖子不断受到关注,因此我决定对其进行扩展并参考此处发布的其他优秀解决方案,至少是我个人过去至少使用过一次的解决方案.但是,我鼓励读者查看下面的答案,因为其中可能有一些我已经忘记或不知道的好建议.

This is an expanded solution of an earlier one I posted. Since that post kept getting attention, I decided to expand on it and refer to the other excellent solutions posted here, at least those that I have personally used in the past at least once. I would, however, encourage the reader to look at the answers below because there are probably good suggestions that I have forgotten, or do not know, about.

这篇关于如何打印出向量的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆