用于显示任何容器元素的通用功能



如何将任何容器的项目呈现到屏幕上?

我知道你可以用函数重载,但你会得到很多不同类型容器的函数。

您可以为不同类型的容器获得许多函数

如果使用模板,则不会。

只匹配可以迭代的内容:

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

在线演示

最新更新