[英]Recursive AsString() to print STL containers in C++
I'm trying to write an AsString() function that converts STL containers to string according to my taste. 我正在尝试编写一个AsString()函数,根据我的喜好将STL容器转换为字符串。 Here's the code I've come up with so far:
这是我到目前为止提出的代码:
template<class T>
inline string AsString(const T& v);
template<class First, class Second>
inline string AsString(const pair<First, Second>& p);
template<class Iter>
inline string PrintSequence(const char* delimiters, Iter begin, Iter end) {
string result;
result += delimiters[0];
int size = 0;
for (size = 0; begin != end; ++size, ++begin) {
if (size > 0) {
result += ", ";
}
result += AsString(*begin);
}
result += delimiters[1];
result += StringPrintf("<%d>", size);
return result;
}
#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \
template<class T1, class T2> \
inline string AsString(const Sequence<T1, T2>& seq) { \
return PrintSequence("[]", seq.begin(), seq.end()); \
}
OUTPUT_TWO_ARG_CONTAINER(vector)
OUTPUT_TWO_ARG_CONTAINER(deque)
OUTPUT_TWO_ARG_CONTAINER(list)
template<class First, class Second>
inline string AsString(const pair<First, Second>& p) {
return "(" + AsString(p.first) + ", " + AsString(p.second) + ")";
}
template<class T>
inline string AsString(const T& v) {
ostringstream s;
s << v;
return s.str();
}
As you can see, the basic idea is that AsString()
recursively calls itself on STL containers and then it bottoms out to the usual operator<<()
(the reason I don't want to override operator<<()
is because I don't want to interfere with other libraries that do exactly that). 正如你所看到的,基本的想法是
AsString()
递归方式在STL容器上调用自身,然后它通常operator<<()
(我不想重写operator<<()
的原因是因为我不想干涉其他正确的库)。
Now, AsString()
compiles and works on shallow containers, but not on nested ones: 现在,
AsString()
编译并处理浅容器,但不能嵌套在容器上:
vector<int> v;
v.push_back(1);
v.push_back(2);
AsString(v) == "[1, 2]<2>"; // true
vector<vector<int> > m;
m.push_back(v);
m.push_back(v);
AsString(m) == "[[1, 2]<2>, [1, 2]<2>]<2>"; // Compilation Error!!!
The compiler, for some reason, wants to use operator<<()
when trying to print the elements of `m', despite the fact that I have provided a template specialization for vectors. 出于某种原因,编译器在尝试打印“m”元素时想要使用
operator<<()
,尽管我已经为向量提供了模板特化。
How could I make AsString()
work? 我怎么能让
AsString()
工作?
UPDATE : OK, turns out the order of definitions do matter (at least for this compiler -- gcc 4.4.3). 更新 :好的,结果定义的顺序很重要(至少对于这个编译器 - gcc 4.4.3)。 When I put the macro definitions first, the compiler will correctly pick them up and display a vector of vectors.
当我首先放置宏定义时,编译器将正确地拾取它们并显示向量向量。 Inexplicable.
令人费解的。
The template world is wonderful... and a real trap to the unwary... 模板世界是美妙的...而且是一个真正的陷阱,对于粗心大意......
A specialization, is taking an existing template function and specifying all its arguments. 专业化,是采用现有的模板函数并指定其所有参数。
An overload is reusing the same name that another function (whether template or not) for a different set of arguments. 重载是为另一组参数重用与另一个函数(无论是否是模板)相同的名称。
template <typename T>
void foo(T const& t);
template <>
void foo<int>(int i); // this is a "complete" specialization
template <typename T, typename U>
void foo<std::pair<T,U>>(std::pair<T,U> const& pair);
// this is a "partial" specialization
// and by the way... it does NOT COMPILE
template <typename T, typename U>
void foo(std::pair<T,U> const& pair); // this is an overload
Note the syntactical difference, in the overload there is no <xxxx>
after the identifier ( foo
here). 注意语法差异,在重载中标识符后面没有
<xxxx>
(这里是foo
)。
It is not possible, in C++, to partially specialize a function; 在C ++中,不可能部分地专门化一个函数; that is to leave some genericity in the arguments.
那就是在论证中保留一些通用性。 You can either overload or fully specialize: mandatory reading at this point GotW #49: Template Specialization and Overloading
您可以重载或完全专门化:此时必须阅读GotW#49:模板专业化和重载
Therefore, the choice is between: 因此,选择是:
template <typename T>
std::string AsString(const T& v); // (1)
template <typename T, typename Allocator>
std::string AsString(std::vector<T, Allocator> const& v); // (2)
And the real question is: what is the type of *begin
? 而真正的问题是:
*begin
的类型是什么?
Well, m
is not const-qualified: 好吧,
m
不是const限定的:
Iter
logically is std::vector< std::vector<int> >::iterator
. Iter
逻辑是std::vector< std::vector<int> >::iterator
。 *begin
is thus std::vector<int>&
*begin
的类型因此是std::vector<int>&
So the two overloads are considered with: 因此,考虑两个重载:
T = std::vector<int>
, requires a conversion to const-ref T = std::vector<int>
,需要转换为const-ref T = int, U = std::allocator<int>
, requires a conversion to const-ref T = int, U = std::allocator<int>
,需要转换为const-ref The second should be selected because it's closer to the real type, as far as I understand. 应该选择第二个,因为它更接近真实类型,据我所知。 I tested it with VC++ 2010 and it actually got selected.
我用VC ++ 2010测试了它,它实际上已被选中。
Could you also declare a non-const qualified version of the vector overload and see if it appeases your compiler ? 你能否声明一个非const限定版本的向量重载,看看它是否会使你的编译器安抚? (which I'd like to know the name of, by the way ;) ).
(顺便说一下,我想知道这个名字;))。
You have not provided a specialization, you have overloaded AsString. 你没有提供专业化,你已经重载了 AsString。 As it happens, your later overload isn't preferred over the T const& version.
碰巧,你的后期重载不是T const和版本的首选。
Instead, overload op<< in a special namespace for the various stdlib containers. 相反,在各种stdlib容器的特殊命名空间中重载op << 。 The namespace is important so you don't affect other code, but you will explicitly use it in AsString:
命名空间很重要,因此您不会影响其他代码,但您将在AsString中明确使用它:
namespace make_sure_to_put_these_overloads_in_a_namespace {
// Your PrintSequence adapted to a stream instead of a string:
template<class Iter>
void PrintSequence(std::ostream &s, const char* delim,
Iter begin, Iter end)
{
s << delim[0];
int size = 0;
if (begin != end) {
s << *begin;
++size;
while (++begin != end) {
s << ", " << *begin;
++size;
}
}
s << delim[1] << '<' << size << '>';
}
#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \
template<class T1, class T2> \
std::ostream& operator<<(std::ostream &s, Sequence<T1, T2> const &seq) { \
PrintSequence(s, "[]", seq.begin(), seq.end()); \
return s; \
}
OUTPUT_TWO_ARG_CONTAINER(std::vector)
OUTPUT_TWO_ARG_CONTAINER(std::deque)
OUTPUT_TWO_ARG_CONTAINER(std::list)
// other types
#undef OUTPUT_TWO_ARG_CONTAINER
template<class First, class Second>
std::ostream& operator<<(std::ostream &s, std::pair<First, Second> const &p) { \
s << "(" << p.first << ", " << p.second << ")";
return s;
}
}
template<class T>
std::string AsString(T const &v) {
using namespace make_sure_to_put_these_overloads_in_a_namespace;
std::ostringstream ss;
ss << v;
return ss.str();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.