递归 AsString() 在 C++ 中打印 STL 容器;
我正在尝试编写一个 AsString() 函数,根据我的喜好将 STL 容器转换为字符串。下面是我到目前为止编写的代码:
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();
}
如您所见,基本思想是 AsString()
在 STL 容器上递归地调用自身,然后它降至通常的 operator< ;<()
(我不想覆盖 operator<<()
的原因是因为我不想干扰其他执行此操作的库)。
现在,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!!!
出于某种原因,编译器在尝试时希望使用 operator<<()
打印“m”的元素,尽管我已经提供了向量的模板专门化。
我怎样才能让 AsString()
工作?
更新:好的,事实证明定义的顺序确实很重要(至少对于这个编译器 - gcc 4.4.3)。当我把宏定义放在第一位时,编译器将正确地选择它们并显示向量的向量。令人费解。
I'm trying to write an AsString() function that converts STL containers to string according to my taste. 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).
Now, AsString()
compiles and works on shallow containers, but not on nested ones:
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.
How could I make AsString()
work?
UPDATE: OK, turns out the order of definitions do matter (at least for this compiler -- 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
模板世界是美妙的......对于粗心的人来说是一个真正的陷阱......
专业化是采用现有的模板函数并指定其所有参数。
重载是对不同的参数集重复使用另一个函数(无论是否模板)的相同名称。
请注意语法差异,在重载中,标识符后面没有
(此处为foo
)。在 C++ 中,不可能部分特化一个函数;也就是说,在论点中留下一些通用性。您可以重载或完全专业化:此时强制阅读 GotW #49:模板专业化和重载
因此,选择是:
真正的问题是:
*begin
的类型是什么?嗯,
m
不是 const 限定的:Iter
逻辑上是std::vector
std::vector>::迭代器
。*begin
的类型为std::vector&
因此,这两个重载被考虑为:
T = std:: vector
,需要转换为 const-refT = int, U = std::allocator
,需要转换为 const-ref第二个据我了解,应该选择它,因为它更接近真实类型。我用VC++ 2010测试了一下,确实被选中了。
您还可以声明向量重载的非常量限定版本并查看它是否满足您的编译器要求吗? (顺便说一下,我想知道它的名字;))。
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.
Note the syntactical difference, in the overload there is no
<xxxx>
after the identifier (foo
here).It is not possible, in C++, to partially specialize a function; 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
Therefore, the choice is between:
And the real question is: what is the type of
*begin
?Well,
m
is not const-qualified:Iter
logically isstd::vector< std::vector<int> >::iterator
.*begin
is thusstd::vector<int>&
So the two overloads are considered with:
T = std::vector<int>
, requires a conversion to const-refT = int, U = std::allocator<int>
, requires a conversion to const-refThe 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.
Could you also declare a non-const qualified version of the vector overload and see if it appeases your compiler ? (which I'd like to know the name of, by the way ;) ).
您没有提供专业化,您已经重载了 AsString。碰巧的是,您后来的重载并不比 T const& 更受欢迎。版本。
相反,重载操作<<在各种 stdlib 容器的特殊命名空间 中。命名空间很重要,因此您不会影响其他代码,但您将在 AsString 中显式使用它:
You have not provided a specialization, you have overloaded AsString. As it happens, your later overload isn't preferred over the T const& version.
Instead, overload op<< in a special namespace for the various stdlib containers. The namespace is important so you don't affect other code, but you will explicitly use it in AsString: