本文介绍了漂亮打印C ++ STL容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 请注意本文末尾的更新。 更新: =http://louisdx.github.com/cxx-prettyprint/>这个图书馆的GitHub上的公开项目 我想要一个单一的模板,通过运算符<< 来处理漂亮打印所有STL容器。 。在伪代码中,我正在寻找这样的: template< container C,class T,String delim = ,String open =[,String close =]> std :: ostream&运算符<<<(std :: ostream& o,const C< T& x) { o&打开; // for(typename C :: const_iterator i = x.begin(); i!= x.end(); i ++)/ * Old-school * / for(auto i = x。 begin(); i!= x.end(); i ++) { if(i!= x.begin())o<分隔符 o<< *一世; } o<<关; return o; } 现在我已经看到了许多模板魔法,可能的,所以我想知道是否有人可以建议一些将匹配所有容器C.可能是一些trait-ish,可以弄清楚如果有一个必要的迭代器? 非常感谢! 更新(和解决方案) 在频道9 上再次提出此问题后,我得到了一个梦幻般的答案从斯文Groot,结合一点SFINAE类型traiting,似乎解决了这个问题在一个完全一般和可嵌套的方式。分隔符可以单独指定,包括std :: set的示例专用化,以及使用自定义分隔符的示例。 帮助器wrap_array()可用于打印原始C数组。 更新:对和元组可用于打印;默认分隔符是圆括号。 enable-if类型trait需要C ++ 0x,但是有一些修改应该可以使C ++ 98版本这个。元组需要可变参数模板,因此C ++ 0x。 我要求Sven在这里发布解决方案,以便我可以接受它,但在此期间,我想发布代码自己参考。 (更新: Sven现在发布了他的代码,这是我接受的答案。我自己的代码使用容器类型traits,它为我工作,但可能会导致非容器类提供迭代器) 标题(prettyprint.h): code> #ifndef H_PRETTY_PRINT #define H_PRETTY_PRINT #include< type_traits> #include< iostream> #include< utility> #include< tuple> 命名空间std { //预先声明容器类型,所以如果不需要,我们实际上不必包含相关的头文件,加速编译时间。 template< typename T,typename TTraits,typename TAllocator>类集; } 命名空间pretty_print { // SFINAE类型trait根据是否存在T :: const_iterator来检测容器。 //(改进想法:检查begin()/ end()是否存在) template< typename T> struct is_container_helper { private: template< typename C> static char test(typename C :: const_iterator *); template< typename C>静态int测试(...); public: static const bool value = sizeof(test< T>(0))== sizeof(char); }; //基本is_container模板;专门从所有期望的容器类型的std :: true_type派生 template< typename T> struct is_container:public :: std :: integral_constant< bool,is_container_helper< T> :: value> {}; //保存特定字符类型的分隔符值 模板< typename TChar> struct delimiters_values { typedef TChar char_type; const TChar * prefix; const TChar * delimiter; const TChar * postfix; }; //定义特定容器和字符类型的分隔符值 template< typename T,typename TChar> struct delimiters { typedef delimiters_values< TChar>类型; static const type values; }; //默认分隔符 模板< typename T> struct delimiters< T,char> {static const delimiters_values< char>值; }; template< typename T> const delimiters_values< char> delimiters< T,char> :: values = {[,,,]}; template< typename T> struct delimiters< T,wchar_t> {static const delimiters_values< wchar_t>值; }; template< typename T> const delimiters_values< wchar_t>分隔符< T,wchar_t> :: values = {L[,L,,L]}; //集合的分隔符 模板< typename T,typename TTraits,typename TAllocator> struct delimiters& :: std :: set< T,TTraits,TAllocator>,char> {static const delimiters_values< char>值; }; template< typename T,typename TTraits,typename TAllocator> const delimiters_values< char>分隔符:: std :: set< T,TTraits,TAllocator>,char> :: values = {{,,,}}; template< typename T,typename TTraits,typename TAllocator> struct delimiters& :: std :: set< T,TTraits,TAllocator>,wchar_t> {static const delimiters_values< wchar_t>值; }; template< typename T,typename TTraits,typename TAllocator> const delimiters_values< wchar_t>分隔符:: std :: set< T,TTraits,TAllocator>,wchar_t> :: values = {L{,L,,L}}; //对的分隔符(对于元组重复使用,见下文) 模板< typename T1,typename T2> struct delimiters& :: std :: pair< T1,T2>,char> {static const delimiters_values< char>值; }; template< typename T1,typename T2> const delimiters_values< char>分隔符:: std :: pair< T1,T2>,char> :: values = {(,,,)}; template< typename T1,typename T2> struct delimiters& :: std :: pair< T1,T2>,wchar_t> {static const delimiters_values< wchar_t>值; }; template< typename T1,typename T2> const delimiters_values< wchar_t>分隔符:: std :: pair< T1,T2>,wchar_t> :: values = {L(,L,,L)}; //打印容器的函子。如果要指定非默认分隔符类型,可以直接使用它。 template< typename T,typename TChar = char,typename TCharTraits = :: std :: char_traits< TChar> ;, typename TDelimiters = delimiters< T,TChar> struct print_container_helper { typedef TChar char_type; typedef TDelimiters delimiters_type; typedef std :: basic_ostream< TChar,TCharTraits> & ostream_type; print_container_helper(const T& container):_container(container) {} inline void operator & stream)const { if(delimiters_type :: values.prefix!= NULL) stream<< delimiters_type :: values.prefix; for(typename T :: const_iterator beg = _container.begin(),end = _container.end(),it = beg; it!= end; ++ it) { if(it!= beg&& delimiters_type :: values.delimiter!= NULL) stream<< delimiters_type :: values.delimiter; stream<< *它; } if(delimiters_type :: values.postfix!= NULL) stream<< delimiters_type :: values.postfix; } private: const T& _容器; }; //类型删除助手类,以方便使用自定义分隔符。 //需要TCharTraits = std :: char_traits< TChar>和TChar = char或wchar_t,MyDelims需要为TChar定义。 //用法:cout<< pretty_print :: custom_delims< MyDelims>(x)。 struct custom_delims_base { virtual〜custom_delims_base(){} virtual :: std :: ostream& stream(:: std :: ostream&)= 0; virtual :: std :: wostream& stream(:: std :: wostream&)= 0; }; template< typename T,typename Delims> struct custom_delims_wrapper:public custom_delims_base { custom_delims_wrapper(const T& t):t(t){} :: std :: ostream& stream(:: std :: ostream& stream) { return stream<< :: pretty_print :: print_container_helper< T,char,:: std :: char_traits< char>,Delims>(t); } :: std :: wostream& stream(:: std :: wostream& stream) { return stream<< :: pretty_print :: print_container_helper< T,wchar_t,:: std :: char_traits< wchar_t> ;, Delims>(t); } private: const T& t; }; template< typename Delims> struct custom_delims { template< typename Container> custom_delims(const Container& c):base(new custom_delims_wrapper< Container,Delims>(c)){} 〜custom_delims } custom_delims_base * base; }; } //命名空间pretty_print 模板< typename TChar,typename TCharTraits,typename Delims> inline std :: basic_ostream< TChar,TCharTraits> & <<<<<<(std :: basic_ostream< TChar,TCharTraits& stream,const pretty_print :: custom_delims< Delims>& p) { return p.base-& ; } // char和wchar_t分隔符的模板别名 //如果您有编译器支持,请启用这些别名 // / / Implement astemplate< T,C,A> const sdelims :: type sdelims< std :: set< T,C,A> :: values = {...} // template< typename T>使用pp_sdelims = pretty_print :: delimiters< T,char> ;; // template< typename T>使用pp_wsdelims = pretty_print :: delimiters< T,wchar_t> ;; 命名空间std { //打印一个print_container_helper到指定的流。 template< typename T,typename TChar,typename TCharTraits,typename TDelimiters> inline basic_ostream< TChar,TCharTraits> &运算符<<<<<<<(basic_stream< TChar,TCharTraits>& stream, const :: pretty_print :: print_container_helper< T,TChar,TCharTraits,TDelimiters& helper) { helper流); return stream; } //使用默认分隔符打印一个容器 template< typename T,typename TChar,typename TCharTraits> inline typename enable_if< :: pretty_print :: is_container< T> :: value,basic_ostream< TChar,TCharTraits>&> :: type operator<<<(basic_ostream< TChar,TCharTraits& stream,const T& container ) {返回流<< :: pretty_print :: print_container_helper< T,TChar,TCharTraits>(container); } //使用分隔符< std :: pair< T1,T2>>打印一个使用分隔符的流。 template< typename T1,typename T2,typename TChar,typename TCharTraits> inline basic_ostream< TChar,TCharTraits> &运算符<<<<<<<<(basic_stream< TChar,TCharTraits>& stream,const pair< T1,T2& value) { if(:: pretty_print :: delimiters< pair< T1,T2& ,TChar> :: values.prefix!= NULL) stream<< :: pretty_print :: delimiters< pair< T1,T2>,TChar> :: values.prefix; stream<< value.first; if(:: pretty_print :: delimiters< pair< T1,T2> TChar> :: values.delimiter!= NULL) stream<< :: pretty_print :: delimiters< pair< T1,T2>,TChar> :: values.delimiter; stream<< value.second; if(:: pretty_print :: delimiters< pair< T1,T2> TChar> :: values.postfix!= NULL) stream<< :: pretty_print :: delimiters< pair< T1,T2>,TChar> :: values.postfix; return stream; } } // namespace std //使用分隔符< std :: pair< tuple_dummy_t,tuple_dummy_t>>打印一个元组到流。 namespace pretty_print { struct tuple_dummy_t {}; //只是想要元组的特殊分隔符。 typedef std :: pair< tuple_dummy_t,tuple_dummy_t> tuple_dummy_pair; template< typename Tuple,size_t N,typename TChar,typename TCharTraits> struct pretty_tuple_helper { static inline void print(:: std :: basic_ostream< TChar,TCharTraits>& stream,const Tuple& value) { pretty_tuple_helper< Tuple,N-1,TChar,TCharTraits> :: print(stream,value); if(delimiters< tuple_dummy_pair,TChar> :: values.delimiter!= NULL) stream<分隔符< tuple_dummy_pair,TChar> :: values.delimiter; stream<< std :: get< N-1>(value); } }; template< typename Tuple,typename TChar,typename TCharTraits> struct pretty_tuple_helper< Tuple,1,TChar,TCharTraits> { static inline void print(:: std :: basic_ostream< TChar,TCharTraits>& stream,const Tuple& value){stream< :: std :: get< 0>(value); } }; } // namespace pretty_print namespace std { template< typename TChar,typename TCharTraits,typename ... Args> inline basic_ostream< TChar,TCharTraits> &运算符<<<<<<<(basic_ostream< TChar,TCharTraits>& stream,const tuple< Args ...& value) { if(:: pretty_print :: delimiters& :tuple_dummy_pair,TChar> :: values.prefix!= NULL) stream<< :: pretty_print :: delimiters< :: pretty_print :: tuple_dummy_pair,TChar> :: values.prefix; :: pretty_print :: pretty_tuple_helper< const tuple< Args ...> & sizeof ...(Args),TChar,TCharTraits> :: print(stream,value); if(:: pretty_print :: delimiters< :: pretty_print :: tuple_dummy_pair,TChar> :: values.postfix!= NULL) stream< :: pretty_print :: delimiters< :: pretty_print :: tuple_dummy_pair,TChar> :: values.postfix; return stream; } } //命名空间std //原始C风格数组的包装器。用法:int arr [] = {1,2,4,8,16}; std :: cout< wrap_array(arr)< ... namespace pretty_print { template< typename T,size_t N> struct array_wrapper { typedef const T * const_iterator; typedef T value_type; array_wrapper(const T(& a)[N]):_array(a){} inline const_iterator begin()const {return _array; } inline const_iterator end()const {return _array + N; } private: const T * const _array; }; } // namespace pretty_print template< typename T,size_t N> inline pretty_print :: array_wrapper< T,N> pretty_print_array(const T(& a)[N]) { return pretty_print :: array_wrapper< T,N& } #endif >使用示例: #include< iostream> #include< vector> #include< unordered_map> #include< map> #include< set> #include< array> #include< tuple> #include< utility> #include< string> #includeprettyprint.h //特定容器的专用化 template<> const pretty_print :: delimiters_values< char> pretty_print :: delimiters< std :: vector< double>,char> :: values = {||,:,|| }; //一次性使用的自定义分隔符 struct MyDel {static const delimiters_values< char>值; }; const delimiters_values< char> MyDel :: values = {<,;,> }; int main(int argc,char * argv []) { std :: string cs; std :: unordered_map< int,std :: string> um; std :: map< int,std :: string> m std :: set< std :: string> ss; std :: vector< std :: string> v; std :: vector< std :: vector< std :: string>> vv; std :: vector< std :: pair< int,std :: string>> vp; std :: vector< double> vd; v.reserve(argc - 1); vv.reserve(argc - 1); vp.reserve(argc - 1); vd.reserve(argc - 1); std :: cout<< 打印对。 << std :: endl; while(--argc) { std :: string s(argv [argc]); std :: pair< int,std :: string> p(argc,s); um [argc] = s; om [argc] = s; v.push_back(s); vv.push_back(v); vp.push_back(p); vd.push_back(1./double(i)); ss.insert(s); cs + = s; std :: cout<< < p } std :: array< char,5> a {{'h','e','l','l','o'}}; std :: cout<< Vector:< v<< std :: endl << Incremental vector:<< vv< std :: endl << 另一个向量:< vd<< std :: endl << 对:< vp<< std :: endl << Set:<< ss << OMap:< om << UMap:< um << String:< cs << Array:< a<< std :: endl ; //手动使用自定义分隔符: std :: cout<< pretty_print :: print_container_helper< std :: vector< std :: string>,char,std :: char_traits< char>,MyDel>(v)< std :: endl; //使用自定义分隔符和类型删除助手类 std :: cout<< pretty_print :: custom_delims< MyDel>(v)<< std :: endl; //对和元组和数组: auto a1 = std :: make_pair(std :: string(Jello),9); auto a2 = std :: make_tuple(1729); auto a3 = std :: make_tuple(Qrgh,a1,11); auto a4 = std :: make_tuple(1729,2875,std :: pair< double,std :: string>(1.5,meow)) int arr [] = {1,4,9,16}; std :: cout<< C array:< wrap_array(arr)< std :: endl << Pair:<< a1 << 1-tuple:< a2 << n-tuple:< a3<< std :: endl < n-tuple:< a4<< std :: endl ; } 改进的其他想法: 在同一个实现输出 std :: tuple< ...> 方式是我们有 std :: pair< S,T> 。更新:现在是 SO的独立问题! Upupdate:现在已经实现了,感谢Xeo! 添加命名空间,使得帮助类不会泄漏到全局命名空间。 / s> 添加模板别名(或类似内容)以方便创建自定义分隔符类或预处理宏。 最近更新: 自定义输出迭代器,在打印函数中使用一个简单的for循环。 所有实现细节现在都在 pretty_print 命名空间中。 修复了命名空间,使得只包含全局流运算符和 pretty_print_array 注意: 移除输出迭代器意味着没有办法使用 std :: copy()来获得漂亮打印。 这是一个有意识的设计决策,使分隔符编译时常量而不是对象常量。这意味着你不能在运行时动态提供分隔符,但它也意味着没有不必要的开销。基于对象的分隔符配置由Dennis Zickefoose在下面对Sven的代码的评论中提出。 目前不清楚如何自定义嵌套容器分隔符。 请记住,此库的目的是允许您需要零编码的快速容器打印设施。它不是一个通用的格式化库,而是一个开发工具,可以减少编写用于容器检查的代码库代码。 >如果您正在寻找一种快速部署自定义分隔符的方法,这里是使用类型擦除的一种方法。我们假设你已经构造了一个分隔符类,例如 MyDel ,如下所示: struct MyDel {static const pretty_print :: delimiters_values< char>值; }; const pretty_print :: delimiters_values< char> MyDel :: values = {<,;,> }; 现在我们要能够写 std :: cout< ; MyPrinter(v)<< std :: endl; 使用这些分隔符,对于某些容器 v MyPrinter 将是一个类型擦除类,如下所示: struct wrapper_base { virtual〜wrapper_base(){} virtual std :: ostream& stream(std :: ostream& o)= 0; }; template< typename T,typename Delims> struct wrapper:public wrapper_base { wrapper(const T& t):t(t){} std :: ostream& stream(std :: ostream& o) { return o<< pretty_print :: print_container_helper< T,char,std :: char_traits< char>,Delims>(t); } private: const T& t; }; template< typename Delims> struct MyPrinter { template< typename Container> MyPrinter(const Container& c):base(new wrapper< Container,Delims>(c)){} 〜MyPrinter } wrapper_base * base; }; template< typename Delims> std :: ostream& operator<<<(std :: ostream& o,const MyPrinter< Delims && p){return p.base-> stream(o); }这个解决方案的灵感来自于Marcelo的解决方案,其中包含了一个用于解决方案的解决方案。很少更改: #include< iostream> #include< iterator> #include< type_traits> #include< vector> #include< algorithm> //这类似于ostream_iterator,但不会在最后一个项目之后打印分隔符 template< typename T,typename TChar = char,typename TCharTraits = std :: char_traits< TChar> ; > class pretty_ostream_iterator:public std :: iterator< std :: output_iterator_tag,void,void,void,void> { public: typedef TChar char_type; typedef TCharTraits traits_type; typedef std :: basic_ostream< TChar,TCharTraits> ostream_type; pretty_ostream_iterator(ostream_type& stream,const char_type * delim = NULL):_stream(& stream),_delim(delim),_insertDelim(false) {} pretty_ostream_iterator< T,TChar,TCharTraits>& operator =(const T& value) { if(_delim!= NULL) { //如果这是第一次使用,不要插入分隔符被称为 if(_insertDelim)(* _stream)<< _delim; else _insertDelim = true; } (* _stream)<<值; return * this; } pretty_ostream_iterator< T,TChar,TCharTraits& operator *() { return * this; } pretty_ostream_iterator< T,TChar,TCharTraits& operator ++() { return * this; } pretty_ostream_iterator< T,TChar,TCharTraits& operator ++(int) { return * this; } private: ostream_type * _stream; const char_type * _delim; bool _insertDelim; }; #if _MSC_VER> = 1400 //将pretty_ostream_iterator声明为checked template< typename T,typename TChar,typename TCharTraits> struct std :: _ Is_checked_helper< pretty_ostream_iterator< T,TChar,TCharTraits> > :public std :: tr1 :: true_type {}; #endif // _MSC_VER> = 1400 命名空间std { //预先声明容器类型,如果不需要,必须包括相关的头文件,从而加快编译时间。 //如果你真的包含头部,这些都不是必需的。 template< typename T,typename TAllocator>类向量; template< typename T,typename TAllocator>类列表; template< typename T,typename TTraits,typename TAllocator>类集; template< typename TKey,typename TValue,typename TTraits,typename TAllocator>类映射; } //基本is_container模板;专门从所有期望的容器类型的std :: true_type派生 template< typename T> struct is_container:public std :: false_type {}; //将向量标记为容器 template< typename T,typename TAllocator> struct is_container< std :: vector< T,TAllocator> > :public std :: true_type {}; //将列表标记为容器 template< typename T,typename TAllocator> struct is_container< std :: list< T,TAllocator> > :public std :: true_type {}; //标记为容器 template< typename T,typename TTraits,typename TAllocator> struct is_container< std :: set< T,TTraits,TAllocator> > :public std :: true_type {}; //将地图标记为容器模板< typename TKey,typename TValue,typename TTraits,typename TAllocator> struct is_container< std :: map< TKey,TValue,TTraits,TAllocator> > :public std :: true_type {}; //保存特定字符类型的分隔符值 template< typename TChar> struct delimiters_values { typedef TChar char_type; const TChar * prefix; const TChar * delimiter; const TChar * postfix; }; //定义特定容器和字符类型的分隔符值 template< typename T,typename TChar> struct delimiters { static const delimiters_values< TChar>值; }; //默认分隔符 template< typename T> struct delimiters< T,char> {static const delimiters_values< char>值; }; template< typename T> const delimiters_values< char>分隔符< T,char> :: values = {{,,,}}; template< typename T> struct delimiters< T,wchar_t> {static const delimiters_values< wchar_t>值; }; template< typename T> const delimiters_values< wchar_t>分隔符< T,wchar_t> :: values = {L{,L,,L}}; //集合的分隔符模板< typename T,typename TTraits,typename TAllocator> struct delimiters< std :: set< T,TTraits,TAllocator>,char> {static const delimiters_values< char>值; }; template< typename T,typename TTraits,typename TAllocator> const delimiters_values< char> delimiters< std :: set< T,TTraits,TAllocator>,char> :: values = {[,,,]}; template< typename T,typename TTraits,typename TAllocator> struct delimiters< std :: set< T,TTraits,TAllocator>,wchar_t> {static const delimiters_values< wchar_t>值; }; template< typename T,typename TTraits,typename TAllocator> const delimiters_values< wchar_t>分隔符< std :: set< T,TTraits,TAllocator>,wchar_t> :: values = {L[,L,,L]}; //对的分隔符模板< typename T1,typename T2> struct delimiters< std :: pair< T1,T2>,char> {static const delimiters_values< char>值; }; template< typename T1,typename T2> const delimiters_values< char> delimiters< std :: pair< T1,T2>,char> :: values = {(,,,)}; template< typename T1,typename T2> struct delimiters< std :: pair< T1,T2>,wchar_t> {static const delimiters_values< wchar_t>值; }; template< typename T1,typename T2> const delimiters_values< wchar_t> delimiters< std :: pair< T1,T2>,wchar_t> :: values = {L(,L,,L)}; //打印容器的函数。如果要指定非默认分隔符类型,可以直接使用它。 template< typename T,typename TChar = char,typename TCharTraits = std :: char_traits< TChar> ;, typename TDelimiters = delimiters< T,TChar> > struct print_container_helper { typedef TChar char_type; typedef TDelimiters delimiters_type; typedef std :: basic_ostream< TChar,TCharTraits>& ostream_type; print_container_helper(const T& container):_container(& container) {} void operator ostream_type& stream)const { if(delimiters_type :: values.prefix!= NULL) stream<< delimiters_type :: values.prefix; std :: copy(_container-> begin(),_container-> end(),pretty_ostream_iterator< typename T :: value_type,TChar,TCharTraits>(stream,delimiters_type :: values.delimiter) if(delimiters_type :: values.postfix!= NULL) stream<< delimiters_type :: values.postfix; } private: const T * _container; }; //将print_container_helper打印到指定的流。 template< typename T,typename TChar,typename TCharTraits,typename TDelimiters> std :: basic_ostream< TChar,TCharTraits>& operator<<<<(std :: basic_ostream< TChar,TCharTraits>& stream,const print_container_helper< T,TChar,TDelimiters& helper) { helper return stream; } // Prints a container to the stream using default delimiters template<typename T, typename TChar, typename TCharTraits> typename std::enable_if<is_container<T>::value, std::basic_ostream<TChar, TCharTraits>&>::type operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const T &container) { stream << print_container_helper<T, TChar, TCharTraits>(container); return stream; } // Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>. template<typename T1, typename T2, typename TChar, typename TCharTraits> std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const std::pair<T1, T2> &value) { if( delimiters<std::pair<T1, T2>, TChar>::values.prefix != NULL ) stream << delimiters<std::pair<T1, T2>, TChar>::values.prefix; stream<< value.first; if( delimiters<std::pair<T1, T2>, TChar>::values.delimiter != NULL ) stream << delimiters<std::pair<T1, T2>, TChar>::values.delimiter; stream<< value.second; if( delimiters<std::pair<T1, T2>, TChar>::values.postfix != NULL ) stream << delimiters<std::pair<T1, T2>, TChar>::values.postfix; return stream; } // Used by the sample below to generate some values struct fibonacci { fibonacci() : f1(0), f2(1) { } int operator()() { int r = f1 + f2; f1 = f2; f2 = r; return f1; } private: int f1; int f2; }; int main() { std :: vector< int> v; std::generate_n(std::back_inserter(v), 10, fibonacci()); std :: cout<< v<< std :: endl; // Example of using pretty_ostream_iterator directly std::generate_n(pretty_ostream_iterator<int>(std::cout, \";\"), 20, fibonacci()); std :: cout<< std :: endl; } Like Marcelo’s version, it uses an is_container type trait that must be specialized for all containers that are to be supported. It may be possible to use a trait to check for value_type, const_iterator, begin()/end(), but I’m not sure I’d recommend that since it might match things that match those criteria but aren’t actually containers, like std::basic_string. Also like Marcelo’s version, it uses templates that can be specialized to specify the delimiters to use. The major difference is that I’ve built my version around a pretty_ostream_iterator, which works similar to the std::ostream_iterator but doesn’t print a delimiter after the last item. Formatting the containers is done by the print_container_helper, which can be used directly to print containers without an is_container trait, or to specify a different delimiters type. I’ve also defined is_container and delimiters so it will work for containers with non-standard predicates or allocators, and for both char and wchar_t. The operator<< function itself is also defined to work with both char and wchar_t streams. Finally, I’ve used std::enable_if, which is available as part of C++0x, and works in Visual C++ 2010 and g++ 4.3 (needs the -std=c++0x flag) and later. This way there is no dependency on Boost. Please take note of the updates at the end of this post.Update: I have created a public project on GitHub for this library!I would like to have a single template that once and for all takes care of pretty-printing all STL containers via operator<<. In pseudo code, I'm looking for something like this:template<container C, class T, String delim = ", ", String open = "[", String close = "]">std::ostream & operator<<(std::ostream & o, const C<T> & x){ o << open; // for (typename C::const_iterator i = x.begin(); i != x.end(); i++) /* Old-school */ for (auto i = x.begin(); i != x.end(); i++) { if (i != x.begin()) o << delim; o << *i; } o << close; return o;}Now I've seen plenty of template magic here on SO that I never thought possible, so I'm wondering if anyone can suggest something that would match all containers C. Maybe something trait-ish that can figure out if something has the necessary iterator?Many thanks!Update (and solution)After raising this problem again on Channel 9, I got a fantastic answer from Sven Groot, which, combined with a bit of SFINAE type traiting, appears to solve the problem in a completely general and nestable fashion. The delimiters may be individually specialised, an example specialization for std::set is included, as well as an example of using custom delimiters.The helper "wrap_array()" can be used to print raw C arrays. Update: Pairs and tuples are available for printing; default delimiters are round brackets.The enable-if type trait requires C++0x, but with some modifications it should be possible to make a C++98 version of this. Tuples require variadic templates, hence C++0x.I have asked Sven to post the solution here so that I can accept it, but in the meantime I'd like to post the code myself for reference. (Update: Sven has now posted his code below, which I made the accepted answer. My own code uses container type traits, which work for me but may cause unexpected behaviour with non-container classes that provide iterators.)Header (prettyprint.h):#ifndef H_PRETTY_PRINT#define H_PRETTY_PRINT#include <type_traits>#include <iostream>#include <utility>#include <tuple>namespace std{ // Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time. template<typename T, typename TTraits, typename TAllocator> class set;}namespace pretty_print{ // SFINAE type trait to detect a container based on whether T::const_iterator exists. // (Improvement idea: check also if begin()/end() exist.) template<typename T> struct is_container_helper { private: template<typename C> static char test(typename C::const_iterator*); template<typename C> static int test(...); public: static const bool value = sizeof(test<T>(0)) == sizeof(char); }; // Basic is_container template; specialize to derive from std::true_type for all desired container types template<typename T> struct is_container : public ::std::integral_constant<bool, is_container_helper<T>::value> { }; // Holds the delimiter values for a specific character type template<typename TChar> struct delimiters_values { typedef TChar char_type; const TChar * prefix; const TChar * delimiter; const TChar * postfix; }; // Defines the delimiter values for a specific container and character type template<typename T, typename TChar> struct delimiters { typedef delimiters_values<TChar> type; static const type values; }; // Default delimiters template<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; }; template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "[", ", ", "]" }; template<typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; }; template<typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"[", L", ", L"]" }; // Delimiters for set template<typename T, typename TTraits, typename TAllocator> struct delimiters< ::std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; }; template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters< ::std::set<T, TTraits, TAllocator>, char>::values = { "{", ", ", "}" }; template<typename T, typename TTraits, typename TAllocator> struct delimiters< ::std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; }; template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters< ::std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"{", L", ", L"}" }; // Delimiters for pair (reused for tuple, see below) template<typename T1, typename T2> struct delimiters< ::std::pair<T1, T2>, char> { static const delimiters_values<char> values; }; template<typename T1, typename T2> const delimiters_values<char> delimiters< ::std::pair<T1, T2>, char>::values = { "(", ", ", ")" }; template<typename T1, typename T2> struct delimiters< ::std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; }; template<typename T1, typename T2> const delimiters_values<wchar_t> delimiters< ::std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" }; // Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type. template<typename T, typename TChar = char, typename TCharTraits = ::std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar>> struct print_container_helper { typedef TChar char_type; typedef TDelimiters delimiters_type; typedef std::basic_ostream<TChar, TCharTraits> & ostream_type; print_container_helper(const T & container) : _container(container) { } inline void operator()(ostream_type & stream) const { if (delimiters_type::values.prefix != NULL) stream << delimiters_type::values.prefix; for (typename T::const_iterator beg = _container.begin(), end = _container.end(), it = beg; it != end; ++it) { if (it != beg && delimiters_type::values.delimiter != NULL) stream << delimiters_type::values.delimiter; stream << *it; } if (delimiters_type::values.postfix != NULL) stream << delimiters_type::values.postfix; } private: const T & _container; }; // Type-erasing helper class for easy use of custom delimiters. // Requires TCharTraits = std::char_traits<TChar> and TChar = char or wchar_t, and MyDelims needs to be defined for TChar. // Usage: "cout << pretty_print::custom_delims<MyDelims>(x)". struct custom_delims_base { virtual ~custom_delims_base() { } virtual ::std::ostream & stream(::std::ostream &) = 0; virtual ::std::wostream & stream(::std::wostream &) = 0; }; template <typename T, typename Delims> struct custom_delims_wrapper : public custom_delims_base { custom_delims_wrapper(const T & t) : t(t) { } ::std::ostream & stream(::std::ostream & stream) { return stream << ::pretty_print::print_container_helper<T, char, ::std::char_traits<char>, Delims>(t); } ::std::wostream & stream(::std::wostream & stream) { return stream << ::pretty_print::print_container_helper<T, wchar_t, ::std::char_traits<wchar_t>, Delims>(t); } private: const T & t; }; template <typename Delims> struct custom_delims { template <typename Container> custom_delims(const Container & c) : base(new custom_delims_wrapper<Container, Delims>(c)) { } ~custom_delims() { delete base; } custom_delims_base * base; };} // namespace pretty_printtemplate <typename TChar, typename TCharTraits, typename Delims>inline std::basic_ostream<TChar, TCharTraits> & operator<<(std::basic_ostream<TChar, TCharTraits> & stream, const pretty_print::custom_delims<Delims> & p){ return p.base->stream(stream);}// Template aliases for char and wchar_t delimiters// Enable these if you have compiler support//// Implement as "template<T, C, A> const sdelims::type sdelims<std::set<T,C,A>>::values = { ... }."//template<typename T> using pp_sdelims = pretty_print::delimiters<T, char>;//template<typename T> using pp_wsdelims = pretty_print::delimiters<T, wchar_t>;namespace std{ // Prints a print_container_helper to the specified stream. template<typename T, typename TChar, typename TCharTraits, typename TDelimiters> inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream, const ::pretty_print::print_container_helper<T, TChar, TCharTraits, TDelimiters> & helper) { helper(stream); return stream; } // Prints a container to the stream using default delimiters template<typename T, typename TChar, typename TCharTraits> inline typename enable_if< ::pretty_print::is_container<T>::value, basic_ostream<TChar, TCharTraits>&>::type operator<<(basic_ostream<TChar, TCharTraits> & stream, const T & container) { return stream << ::pretty_print::print_container_helper<T, TChar, TCharTraits>(container); } // Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>. template<typename T1, typename T2, typename TChar, typename TCharTraits> inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream, const pair<T1, T2> & value) { if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.prefix != NULL) stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.prefix; stream << value.first; if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.delimiter != NULL) stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.delimiter; stream << value.second; if (::pretty_print::delimiters<pair<T1, T2>, TChar>::values.postfix != NULL) stream << ::pretty_print::delimiters<pair<T1, T2>, TChar>::values.postfix; return stream; }} // namespace std// Prints a tuple to the stream using delimiters from delimiters<std::pair<tuple_dummy_t, tuple_dummy_t>>.namespace pretty_print{ struct tuple_dummy_t { }; // Just if you want special delimiters for tuples. typedef std::pair<tuple_dummy_t, tuple_dummy_t> tuple_dummy_pair; template<typename Tuple, size_t N, typename TChar, typename TCharTraits> struct pretty_tuple_helper { static inline void print(::std::basic_ostream<TChar, TCharTraits> & stream, const Tuple & value) { pretty_tuple_helper<Tuple, N - 1, TChar, TCharTraits>::print(stream, value); if (delimiters<tuple_dummy_pair, TChar>::values.delimiter != NULL) stream << delimiters<tuple_dummy_pair, TChar>::values.delimiter; stream << std::get<N - 1>(value); } }; template<typename Tuple, typename TChar, typename TCharTraits> struct pretty_tuple_helper<Tuple, 1, TChar, TCharTraits> { static inline void print(::std::basic_ostream<TChar, TCharTraits> & stream, const Tuple & value) { stream << ::std::get<0>(value); } };} // namespace pretty_printnamespace std{ template<typename TChar, typename TCharTraits, typename ...Args> inline basic_ostream<TChar, TCharTraits> & operator<<(basic_ostream<TChar, TCharTraits> & stream, const tuple<Args...> & value) { if (::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.prefix != NULL) stream << ::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.prefix; ::pretty_print::pretty_tuple_helper<const tuple<Args...> &, sizeof...(Args), TChar, TCharTraits>::print(stream, value); if (::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.postfix != NULL) stream << ::pretty_print::delimiters< ::pretty_print::tuple_dummy_pair, TChar>::values.postfix; return stream; }} // namespace std// A wrapper for raw C-style arrays. Usage: int arr[] = { 1, 2, 4, 8, 16 }; std::cout << wrap_array(arr) << ...namespace pretty_print{ template <typename T, size_t N> struct array_wrapper { typedef const T * const_iterator; typedef T value_type; array_wrapper(const T (& a)[N]) : _array(a) { } inline const_iterator begin() const { return _array; } inline const_iterator end() const { return _array + N; } private: const T * const _array; };} // namespace pretty_printtemplate <typename T, size_t N>inline pretty_print::array_wrapper<T, N> pretty_print_array(const T (& a)[N]){ return pretty_print::array_wrapper<T, N>(a);}#endifUsage example:#include <iostream>#include <vector>#include <unordered_map>#include <map>#include <set>#include <array>#include <tuple>#include <utility>#include <string>#include "prettyprint.h"// Specialization for a particular containertemplate<> const pretty_print::delimiters_values<char> pretty_print::delimiters<std::vector<double>, char>::values = { "|| ", " : ", " ||" };// Custom delimiters for one-off usestruct MyDel { static const delimiters_values<char> values; };const delimiters_values<char> MyDel::values = { "<", "; ", ">" };int main(int argc, char * argv[]){ std::string cs; std::unordered_map<int, std::string> um; std::map<int, std::string> om; std::set<std::string> ss; std::vector<std::string> v; std::vector<std::vector<std::string>> vv; std::vector<std::pair<int, std::string>> vp; std::vector<double> vd; v.reserve(argc - 1); vv.reserve(argc - 1); vp.reserve(argc - 1); vd.reserve(argc - 1); std::cout << "Printing pairs." << std::endl; while (--argc) { std::string s(argv[argc]); std::pair<int, std::string> p(argc, s); um[argc] = s; om[argc] = s; v.push_back(s); vv.push_back(v); vp.push_back(p); vd.push_back(1./double(i)); ss.insert(s); cs += s; std::cout << " " << p << std::endl; } std::array<char, 5> a{{ 'h', 'e', 'l', 'l', 'o' }}; std::cout << "Vector: " << v << std::endl << "Incremental vector: " << vv << std::endl << "Another vector: " << vd << std::endl << "Pairs: " << vp << std::endl << "Set: " << ss << std::endl << "OMap: " << om << std::endl << "UMap: " << um << std::endl << "String: " << cs << std::endl << "Array: " << a << std::endl ; // Using custom delimiters manually: std::cout << pretty_print::print_container_helper<std::vector<std::string>, char, std::char_traits<char>, MyDel>(v) << std::endl; // Using custom delimiters with the type-erasing helper class std::cout << pretty_print::custom_delims<MyDel>(v) << std::endl; // Pairs and tuples and arrays: auto a1 = std::make_pair(std::string("Jello"), 9); auto a2 = std::make_tuple(1729); auto a3 = std::make_tuple("Qrgh", a1, 11); auto a4 = std::make_tuple(1729, 2875, std::pair<double, std::string>(1.5, "meow")); int arr[] = { 1, 4, 9, 16 }; std::cout << "C array: " << wrap_array(arr) << std::endl << "Pair: " << a1 << std::endl << "1-tuple: " << a2 << std::endl << "n-tuple: " << a3 << std::endl << "n-tuple: " << a4 << std::endl ;}Further ideas for improvements:Implement output for std::tuple<...> in the same way is we have it for std::pair<S,T>. Update: This is now a separate question on SO! Upupdate: This has now been implemented, thanks to Xeo!Add namespaces so that the helper classes don't bleed into the global namespace. DoneAdd template aliases (or something similar) to facilitate making custom delimiter classes, or maybe preprocessor macros?Recent updates:I removed the custom output iterator in favour of a simple for loop in the print function.All implementation details are now in the pretty_print namespace. Only the global stream operators and the pretty_print_array wrapper are in the global namespace.Fixed the namespacing so that operator<< is now correctly in std.Notes:Removing the output iterator means that there is no way to use std::copy() to get pretty-printing. I might reinstate the pretty iterator if this is a desired feature, but Sven's code below has the implementation.It was a conscious design decision to make the delimiters compile-time constants rather than object constants. That means that you cannot supply delimiters dynamically at runtime, but it also means that there's no unneeded overhead. An object-based delimiter configuration has been proposed by Dennis Zickefoose in a comment to Sven's code below. If desired, this could be implemented as an alternative feature.It is currently not obvious how to customize nested container delimiters.Bear in mind that the purpose of this library is to allow quick container printing facilities that require zero coding on your part. It is not an all-purpose formatting library, but rather a developing tool to alleviate the need to write boiler-plate code for container inspection.Thank you to everyone who contributed!Note: If you are looking for a quick way to deploy custom delimiters, here is one way using type erasure. We assume that you have already constructed a delimiter class, say MyDel, like so:struct MyDel { static const pretty_print::delimiters_values<char> values; };const pretty_print::delimiters_values<char> MyDel::values = { "<", "; ", ">" };Now we want to be able to write std::cout << MyPrinter(v) << std::endl; for some container v using those delimiters. MyPrinter will be a type-erasing class, like so:struct wrapper_base{ virtual ~wrapper_base() { } virtual std::ostream & stream(std::ostream & o) = 0;};template <typename T, typename Delims>struct wrapper : public wrapper_base{ wrapper(const T & t) : t(t) { } std::ostream & stream(std::ostream & o) { return o << pretty_print::print_container_helper<T, char, std::char_traits<char>, Delims>(t); }private: const T & t;};template <typename Delims>struct MyPrinter{ template <typename Container> MyPrinter(const Container & c) : base(new wrapper<Container, Delims>(c)) { } ~MyPrinter() { delete base; } wrapper_base * base;};template <typename Delims>std::ostream & operator<<(std::ostream & o, const MyPrinter<Delims> & p) { return p.base->stream(o); } 解决方案 This solution was inspired by Marcelo's solution, with a few changes:#include <iostream>#include <iterator>#include <type_traits>#include <vector>#include <algorithm>// This works similar to ostream_iterator, but doesn't print a delimiter after the final itemtemplate<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar> >class pretty_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void>{public: typedef TChar char_type; typedef TCharTraits traits_type; typedef std::basic_ostream<TChar, TCharTraits> ostream_type; pretty_ostream_iterator(ostream_type &stream, const char_type *delim = NULL) : _stream(&stream), _delim(delim), _insertDelim(false) { } pretty_ostream_iterator<T, TChar, TCharTraits>& operator=(const T &value) { if( _delim != NULL ) { // Don't insert a delimiter if this is the first time the function is called if( _insertDelim ) (*_stream) << _delim; else _insertDelim = true; } (*_stream) << value; return *this; } pretty_ostream_iterator<T, TChar, TCharTraits>& operator*() { return *this; } pretty_ostream_iterator<T, TChar, TCharTraits>& operator++() { return *this; } pretty_ostream_iterator<T, TChar, TCharTraits>& operator++(int) { return *this; }private: ostream_type *_stream; const char_type *_delim; bool _insertDelim;};#if _MSC_VER >= 1400// Declare pretty_ostream_iterator as checkedtemplate<typename T, typename TChar, typename TCharTraits>struct std::_Is_checked_helper<pretty_ostream_iterator<T, TChar, TCharTraits> > : public std::tr1::true_type{};#endif // _MSC_VER >= 1400namespace std{ // Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time. // These aren't necessary if you do actually include the headers. template<typename T, typename TAllocator> class vector; template<typename T, typename TAllocator> class list; template<typename T, typename TTraits, typename TAllocator> class set; template<typename TKey, typename TValue, typename TTraits, typename TAllocator> class map;}// Basic is_container template; specialize to derive from std::true_type for all desired container typestemplate<typename T> struct is_container : public std::false_type { };// Mark vector as a containertemplate<typename T, typename TAllocator> struct is_container<std::vector<T, TAllocator> > : public std::true_type { };// Mark list as a containertemplate<typename T, typename TAllocator> struct is_container<std::list<T, TAllocator> > : public std::true_type { };// Mark set as a containertemplate<typename T, typename TTraits, typename TAllocator> struct is_container<std::set<T, TTraits, TAllocator> > : public std::true_type { };// Mark map as a containertemplate<typename TKey, typename TValue, typename TTraits, typename TAllocator> struct is_container<std::map<TKey, TValue, TTraits, TAllocator> > : public std::true_type { };// Holds the delimiter values for a specific character typetemplate<typename TChar>struct delimiters_values{ typedef TChar char_type; const TChar *prefix; const TChar *delimiter; const TChar *postfix;};// Defines the delimiter values for a specific container and character typetemplate<typename T, typename TChar>struct delimiters{ static const delimiters_values<TChar> values;};// Default delimiterstemplate<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "{ ", ", ", " }" };template<typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };template<typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"{ ", L", ", L" }" };// Delimiters for settemplate<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; };template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters<std::set<T, TTraits, TAllocator>, char>::values = { "[ ", ", ", " ]" };template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters<std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"[ ", L", ", L" ]" };// Delimiters for pairtemplate<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };template<typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };template<typename T1, typename T2> const delimiters_values<wchar_t> delimiters<std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };// Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type.template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar> >struct print_container_helper{ typedef TChar char_type; typedef TDelimiters delimiters_type; typedef std::basic_ostream<TChar, TCharTraits>& ostream_type; print_container_helper(const T &container) : _container(&container) { } void operator()(ostream_type &stream) const { if( delimiters_type::values.prefix != NULL ) stream << delimiters_type::values.prefix; std::copy(_container->begin(), _container->end(), pretty_ostream_iterator<typename T::value_type, TChar, TCharTraits>(stream, delimiters_type::values.delimiter)); if( delimiters_type::values.postfix != NULL ) stream << delimiters_type::values.postfix; }private: const T *_container;};// Prints a print_container_helper to the specified stream.template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const print_container_helper<T, TChar, TDelimiters> &helper){ helper(stream); return stream;}// Prints a container to the stream using default delimiterstemplate<typename T, typename TChar, typename TCharTraits>typename std::enable_if<is_container<T>::value, std::basic_ostream<TChar, TCharTraits>&>::type operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const T &container){ stream << print_container_helper<T, TChar, TCharTraits>(container); return stream;}// Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>.template<typename T1, typename T2, typename TChar, typename TCharTraits>std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const std::pair<T1, T2> &value){ if( delimiters<std::pair<T1, T2>, TChar>::values.prefix != NULL ) stream << delimiters<std::pair<T1, T2>, TChar>::values.prefix; stream << value.first; if( delimiters<std::pair<T1, T2>, TChar>::values.delimiter != NULL ) stream << delimiters<std::pair<T1, T2>, TChar>::values.delimiter; stream << value.second; if( delimiters<std::pair<T1, T2>, TChar>::values.postfix != NULL ) stream << delimiters<std::pair<T1, T2>, TChar>::values.postfix; return stream;}// Used by the sample below to generate some valuesstruct fibonacci{ fibonacci() : f1(0), f2(1) { } int operator()() { int r = f1 + f2; f1 = f2; f2 = r; return f1; }private: int f1; int f2;};int main(){ std::vector<int> v; std::generate_n(std::back_inserter(v), 10, fibonacci()); std::cout << v << std::endl; // Example of using pretty_ostream_iterator directly std::generate_n(pretty_ostream_iterator<int>(std::cout, ";"), 20, fibonacci()); std::cout << std::endl;}Like Marcelo's version, it uses an is_container type trait that must be specialized for all containers that are to be supported. It may be possible to use a trait to check for value_type, const_iterator, begin()/end(), but I'm not sure I'd recommend that since it might match things that match those criteria but aren't actually containers, like std::basic_string. Also like Marcelo's version, it uses templates that can be specialized to specify the delimiters to use.The major difference is that I've built my version around a pretty_ostream_iterator, which works similar to the std::ostream_iterator but doesn't print a delimiter after the last item. Formatting the containers is done by the print_container_helper, which can be used directly to print containers without an is_container trait, or to specify a different delimiters type.I've also defined is_container and delimiters so it will work for containers with non-standard predicates or allocators, and for both char and wchar_t. The operator<< function itself is also defined to work with both char and wchar_t streams.Finally, I've used std::enable_if, which is available as part of C++0x, and works in Visual C++ 2010 and g++ 4.3 (needs the -std=c++0x flag) and later. This way there is no dependency on Boost. 这篇关于漂亮打印C ++ STL容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 08-18 09:24