许多时候,我们想要直接打印容器的内容,比如
std::vector<int> a = { 1, 2, 3 };
可以打印出[1, 2, 3]。
参考标准库,可以写出一个带有迭代器的to_string函数:
template <typename Iter, typename Func>
std::string to_string(Iter begin, Iter end, Func func) {std::string str;str.append("[");if (begin != end) {str.append(func(*begin++));while (begin != end) {str.append(", ");str.append(func(*begin++));}}str.append("]");return str;
}
在调用时候,可以直接传入begin()和end(),作为迭代器,再传入对于元素的to_string函数,即可实现容器的to_string。
std::to_string是C++11新增的标准库中定义的函数,具有多个重载函数(详情请参考to_string - C++Reference):
string to_string (int val); string to_string (long val); string to_string (long long val); string to_string (unsigned val); string to_string (unsigned long val); string to_string (unsigned long long val); string to_string (float val); string to_string (double val); string to_string (long double val);
由于是重载函数,因此不能直接使用名称to_string进行函数的传递。
我们想要使用类似于to_string(vec.begin(), vec.end(), to_string)这种传递方式,怎么办呢?
在C++11中,我们可以传递匿名函数(lambda)解决二义性问题:
用 [](const T &t) { return to_string(t); } 取代 to_string。
to_string(vec.begin(), vec.end(), [](const T &t) { return to_string(t); });
还有一种方法,就是定义函数对象:
template <typename T>
struct ToString
{std::string operator()(const T &t){return to_string(t);}
};
to_string(vec.begin(), vec.end(), ToString<T>());
这在C++11标准之前的代码中经常使用。
借此,我们可以定义一个vector版的to_string函数:
template <typename T>
std::string to_string(const std::vector<T> &vec)
{return to_string(vec.begin(), vec.end(),[](const T &t) { return to_string(t); });
}
// Or
template <typename T>
std::string to_string(const std::vector<T> &vec)
{return to_string(vec.begin(), vec.end(), ToString<T>());
}
然后,就可以这么使用了:
std::vector<int> a = { 1, 2, 3 };std::vector<int> b = { 4, 5, 6 };std::vector<std::vector<int>> c = { a, b };std::cout << to_string(a) << std::endl; // [1, 2, 3]std::cout << to_string(b) << std::endl; // [4, 5, 6]std::cout << to_string(c) << std::endl; // [[1, 2, 3], [4, 5, 6]]
可以输出vector的各种类型(包括vector)。
然而,存在一个问题就是,由于我的这些代码在最初就using namespcae std;了,因此并没有什么问题出现(很好地实现了vector类型的to_string重载)。
但是,当我们把该行去掉后,就会发现,编译器会报出错误。
比如在ToString类的operator()函数中,在
return to_string(t);
这一行,就报出了:
error: no matching function for call to 'to_string(const int&)'
的错误。
似乎解决方法很简单,直接加std::不就行了吗?
然而,我们发现,这种解决方法,在只输出a和b的情况下,确实没什么问题,但是,当输出c的时候,又报出了
error: no matching function for call to 'to_string(const std::vector<int>&)'
的错误。
——怎么办呢?
其实解决起来很简单,就是使用using使得命名空间无效,当然这个using是加在函数里面的。
template <typename T>
struct ToString
{std::string operator()(const T &t){using std::to_string; // 标准库to_stringusing ::to_string; // 自定义to_stringreturn to_string(t);}
};
using ::to_string表示你自定义to_string的位置,如果也是在命名空间里面,如(namespace A),那么就写成using A::to_string;即可。
完整代码如下:
#include <iostream>
#include <string>
#include <vector>template <typename Iter, typename Func>
std::string to_string(Iter begin, Iter end, Func func) {std::string str;str.append("[");if (begin != end) {str.append(func(*begin++));while (begin != end) {str.append(", ");str.append(func(*begin++));}}str.append("]");return str;
}template <typename T>
struct ToString;template <typename T>
std::string to_string(const std::vector<T> &vec)
{return to_string(vec.begin(), vec.end(), ToString<T>());// Or/*return to_string(vec.begin(), vec.end(),[](const T &t) {using std::to_string;using ::to_string;return to_string(t);});*/
}template <typename T>
struct ToString
{std::string operator()(const T &t){using std::to_string;using ::to_string;return to_string(t);}
};int main(void)
{std::vector<int> a = { 1, 2, 3 };std::vector<int> b = { 4, 5, 6 };std::vector<std::vector<int>> c = { a, b };std::cout << to_string(c) << std::endl;return 0;
}