如何将任何容器的项目呈现到屏幕上?
我知道你可以用函数重载,但你会得到很多不同类型容器的函数。
您可以为不同类型的容器获得许多函数
如果使用模板,则不会。
只匹配可以迭代的内容:
template <std::ranges::range R>
std::ostream& operator<<(std::ostream& os, const R & range) {
for (auto & elem : range) {
os << elem;
}
return os;
}
匹配std::pair(用于map等(、std::tuple和其他异构容器:
template <typename T>
concept is_tuple = requires { typename std::tuple_size<T>::type; };
template <is_tuple T, std::size_t... Is>
std::ostream& print_tuple(std::ostream& os, const T & tuple, std::index_sequence<Is...>) {
return os << ... << get<Is>(tuple);
}
template <is_tuple T>
std::ostream& operator<<(std::ostream& os, const T & tuple) {
return print_tuple(os, tuple, std::make_index_sequence<std::tuple_size_v<T>>{});
}
这是一个棘手的问题,我们可以枚举所有容器类型并为它们添加打印功能。对于客户容器,您需要添加宏来添加operator<<
。
这里的代码的灵感来源于googleglog的utils代码,它适用于嵌套容器。
添加了一些内容:
forward_list的- 过载
- std::array的过载
对于作为容器元素的用户定义类型,我们也需要为它们定义operator<<
。
struct CustomType {
CustomType(std::string name, int age) : name_(std::move(name)), age_(age) {}
std::string name_;
int age_;
};
std::ostream& operator<<(std::ostream& out, const CustomType& ct) {
out << "name:" << ct.name_ << ","
<< "age:" << ct.age_;
return out;
}
// Mostly copied from https://github.com/google/glog/blob/master/src/glog/stl_logging.h.in
#include <deque>
#include <forward_list>
#include <iostream>
#include <list>
#include <map>
#include <ostream>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
template <class First, class Second>
std::ostream& operator<<(std::ostream& out, const std::pair<First, Second>& p);
template <class Iter>
std::ostream& PrintSequence(std::ostream& out, Iter begin, Iter end);
#define OUTPUT_TWO_ARG_CONTAINER(Sequence)
template <class T1, class T2>
inline std::ostream& operator<<(std::ostream& out,
const Sequence<T1, T2>& seq) {
return PrintSequence(out, seq.begin(), seq.end());
}
OUTPUT_TWO_ARG_CONTAINER(std::vector)
OUTPUT_TWO_ARG_CONTAINER(std::deque)
OUTPUT_TWO_ARG_CONTAINER(std::list)
OUTPUT_TWO_ARG_CONTAINER(std::forward_list)
#define OUTPUT_THREE_ARG_CONTAINER(Sequence)
template <class T1, class T2, class T3>
inline std::ostream& operator<<(std::ostream& out,
const Sequence<T1, T2, T3>& seq) {
return PrintSequence(out, seq.begin(), seq.end());
}
OUTPUT_THREE_ARG_CONTAINER(std::set)
OUTPUT_THREE_ARG_CONTAINER(std::multiset)
#define OUTPUT_FOUR_ARG_CONTAINER(Sequence)
template <class T1, class T2, class T3, class T4>
inline std::ostream& operator<<(std::ostream& out,
const Sequence<T1, T2, T3, T4>& seq) {
return PrintSequence(out, seq.begin(), seq.end());
}
OUTPUT_FOUR_ARG_CONTAINER(std::map)
OUTPUT_FOUR_ARG_CONTAINER(std::multimap)
OUTPUT_FOUR_ARG_CONTAINER(std::unordered_set)
OUTPUT_FOUR_ARG_CONTAINER(std::unordered_multiset)
#define OUTPUT_FIVE_ARG_CONTAINER(Sequence)
template <class T1, class T2, class T3, class T4, class T5>
inline std::ostream& operator<<(std::ostream& out,
const Sequence<T1, T2, T3, T4, T5>& seq) {
return PrintSequence(out, seq.begin(), seq.end());
}
OUTPUT_FIVE_ARG_CONTAINER(std::unordered_map)
OUTPUT_FIVE_ARG_CONTAINER(std::unordered_multimap)
template <class First, class Second>
inline std::ostream& operator<<(std::ostream& out,
const std::pair<First, Second>& p) {
out << '(' << p.first << ", " << p.second << ')';
return out;
}
template <typename T, size_t N>
inline std::ostream& operator<<(std::ostream& out,
const std::array<T, N>& seq) {
return PrintSequence(out, seq.begin(), seq.end());
}
template <class Iter>
inline std::ostream& PrintSequence(std::ostream& out, Iter begin, Iter end) {
out << '[';
for (int i = 0; begin != end; ++i, ++begin) {
if (i > 0) out << ' ';
out << *begin;
}
if (begin != end) {
out << " ...";
}
out << ']';
return out;
}
int main() {
std::vector vec{1, 2, 3};
std::list lst{1, 2, 3};
std::forward_list fwd_lst{1, 2, 3};
std::unordered_map<std::string, int> map{{"foo", 1}, {"bar", 2}};
std::array<int, 3> arr{1, 2, 3};
std::cout << "print vector:" << vec << 'n';
std::cout << "print forward list:" << fwd_lst << 'n';
std::cout << "print list:" << vec << 'n';
std::cout << "print array:" << arr << 'n';
std::cout << "print unordered_map:" << map << 'n';
std::vector<std::vector<int>> vec2d{{1, 2, 3}, {4, 5, 6}};
std::list<std::unordered_map<std::string, int>> list_of_map{
{{"foo", 1}, {"bar", 2}}, {{"foo2", 1}, {"bar2", 2}}};
std::cout << "2d vector:" << vec2d << 'n';
std::cout << "list of unordered_map:" << list_of_map << 'n';
return 0;
}
输出:
print vector:[1 2 3]
print forward list:[1 2 3]
print list:[1 2 3]
print array:[1 2 3]
print unordered_map:[(bar, 2) (foo, 1)]
2d vector:[[1 2 3] [4 5 6]]
list of unordered_map:[[(bar, 2) (foo, 1)] [(bar2, 2) (foo2, 1)]]
在线演示
还有其他方法,比如使用成员检测器,这样我们就可以避免以前丑陋的宏。
更新:
伟大的fmt
库已经支持它:
#include <map>
#include <vector>
#include <fmt/ranges.h>
int main() {
auto v = std::vector<int>{1, 2, 3};
auto m = std::map<int, int>{{1, 2}};
std::vector<std::vector<int>> vec2d{{1, 2, 3}, {4, 5, 6}};
fmt::print("{}", m);
fmt::print("{}", v);
fmt::print("{}", vec2d);
}
在线演示