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


实现以下操作符<<的解决方案也很好:

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

当前回答

在c++ 11中,基于范围的for循环可能是一个很好的解决方案:

vector<char> items = {'a','b','c'};
for (char n : items)
    cout << n << ' ';

输出:

a b c 

其他回答

对于那些感兴趣的人:我写了一个通用的解决方案,它两全其美,更通用于任何类型的范围,并在非算术类型周围加上引号(适合于类似字符串的类型)。此外,这种方法不应该有任何ADL问题,也可以避免“意外”(因为它是根据具体情况明确添加的):

template <typename T>
inline constexpr bool is_string_type_v = std::is_convertible_v<const T&, std::string_view>;

template<class T>
struct range_out {
  range_out(T& range) : r_(range) {
  }
  T& r_;
  static_assert(!::is_string_type_v<T>, "strings and string-like types should use operator << directly");
};

template <typename T>
std::ostream& operator<< (std::ostream& out, range_out<T>& range) {
  constexpr bool is_string_like = is_string_type_v<T::value_type>;
  constexpr std::string_view sep{ is_string_like ? "', '" : ", " };

  if (!range.r_.empty()) {
    out << (is_string_like ? "['" : "[");
    out << *range.r_.begin();
    for (auto it = range.r_.begin() + 1; it != range.r_.end(); ++it) {
      out << sep << *it;
    }
    out << (is_string_like ? "']" : "]");
  }
  else {
    out << "[]";
  }

  return out;
}

现在它在任何范围都很容易使用:

std::cout << range_out{ my_vector };

类似字符串的检查留有改进的空间。 在我的解决方案中,我也有static_assert检查,以避免std::basic_string<>,但为了简单起见,我在这里省略了它。

for_each + lambda表达式怎么样:

#include <vector>
#include <algorithm>
// ...
std::vector<char> vec;
// ...
std::for_each(
              vec.cbegin(),
              vec.cend(),
              [] (const char c) {std::cout << c << " ";} 
              );
// ...

当然,对于这个具体任务,基于范围的for是最优雅的解决方案,但它也提供了许多其他可能性。

解释

for_each算法接受一个输入范围和一个可调用对象,在范围的每个元素上调用该对象。输入范围由两个迭代器定义。可调用对象可以是一个函数、一个函数指针、一个重载()操作符的类的对象,或者在本例中是一个lambda表达式。此表达式的形参与vector中元素的类型匹配。

这个实现的美妙之处在于lambda表达式的强大功能——您可以使用这种方法做更多的事情,而不仅仅是打印向量。

对于想要没有循环的一行程序的人:

我不敢相信没有人知道这一点,但也许是因为更像c的方法。不管怎样,在没有循环的情况下,假设std::vector<char>是空终止的,在一行程序中是完全安全的:

std::vector<char> test { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\0' };
std::cout << test.data() << std::endl;

但为了安全起见,我会把它包装在ostream操作符中,就像@Zorawar建议的那样:

template <typename T>std::ostream& operator<< (std::ostream& out, std::vector<T>& v)
{
    v.push_back('\0'); // safety-check!
    out << v.data();
    return out;
}

std::cout << test << std::endl; // will print 'Hello, world!'

我们可以通过使用printf来实现类似的行为:

fprintf(stdout, "%s\n", &test[0]); // will also print 'Hello, world!'

注意:

重载的ostream操作符需要接受非const的vector。这可能会使程序不安全或引入不可用的代码。此外,由于添加了空字符,可能会发生std::vector的重新分配。因此,使用带有迭代器的for循环可能会更快。

我看到了两个问题。正如在

for (x = 17; isalpha(firstsquare); x++)

要么是一个无限循环,要么根本没有执行,同样在if(入口== 'S')中,如果入口字符与'S'不同,则没有任何东西被推送到路径向量,使其为空,因此在屏幕上不打印任何东西。您可以检查path.empty()或打印path.size()来测试后者。

不管怎样,使用字符串而不是向量不是更好吗?您可以像访问数组一样访问字符串内容,查找字符,提取子字符串并轻松打印字符串(无需循环)。

用字符串来完成这一切可能是用一种不那么复杂的方式来编写它,并且更容易发现问题。

您可以使用{fmt}库打印容器以及范围和元组。例如:

#include <vector>
#include <fmt/ranges.h>

int main() {
  auto v = std::vector<int>{1, 2, 3};
  fmt::print("{}", v);
}

打印

[1, 2, 3]

到标准输出(godbolt)。

我不建议对标准容器重载操作符<<,因为它可能会引入ODR违规。

声明:我是{fmt}的作者。