許多時候,我們想要直接打印容器的內容,比如
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;
}
?