几乎什么都能转。
把tuple转字符串最难写。
#include <bits/stdc++.h> namespace ext { template <typename StrType> std::string ToString(const StrType& str); template <typename StrType> std::string ToString(const std::vector<StrType>& vec); template <typename... Args> std::string ToString(const std::tuple<Args...>& tpl); template <typename K, typename D, typename A> std::string ToString(const std::set<K, D, A>& value); template <typename K, typename D, typename A> std::string ToString(const std::multiset<K, D, A>& value); template <typename K, typename H, typename D, typename A> std::string ToString(const std::unordered_set<K, H, D, A>& value); template <typename K, typename V, typename D, typename A> std::string ToString(const std::map<K, V, D, A>& value); template <typename K, typename V, typename D, typename A> std::string ToString(const std::multimap<K, V, D, A>& value); template <typename K, typename V, typename H, typename D, typename A> std::string ToString(const std::unordered_map<K, V, H, D, A>& value); template <typename... Args> std::string ToString(const std::pair<Args...>& tpl); std::string ToString(const std::string& value); namespace detail { template <typename Tuple, int pos> struct ToStringImpl { static std::string Impl(const Tuple& tpl) { return ToStringImpl<Tuple, pos - 1>::Impl(tpl).append(ext::ToString(std::get<pos>(tpl))).append(pos + 1 == static_cast<int>(std::tuple_size_v<Tuple>) ? "" : ", "); } }; template <typename Tuple> struct ToStringImpl<Tuple, -1> { static std::string Impl(const Tuple& tpl) { return std::string(); } }; template <typename Tuple> struct ToStringImpl<Tuple, 0> { static std::string Impl(const Tuple& tpl) { return ext::ToString(std::get<0>(tpl)).append(1 == static_cast<int>(std::tuple_size_v<Tuple>) ? "" : ", "); } }; template <typename SetType> std::string ToStringSetImpl(const SetType& value) { std::string res = "{"; for (typename SetType::const_iterator iter = value.begin(); iter != value.end(); iter++) { if (iter != value.begin()) { res.append(", "); } res.append(ToString(*iter)); } return res.append("}"); } template <typename MapType> std::string ToStringMapImpl(const MapType& value) { std::string res = "{"; for (typename MapType::const_iterator iter = value.begin(); iter != value.end(); iter++) { if (iter != value.begin()) { res.append(", "); } res.append(ToString(iter->first)); res.append(": "); res.append(ToString(iter->second)); } return res.append("}"); } } // namespace detail template <typename StrType> std::string ToString(const StrType& str) { std::ostringstream oss; oss << str; return oss.str(); } template <typename StrType> std::string ToString(const std::vector<StrType>& vec) { std::string res = "["; const int n = vec.size(); for (int i = 0; i < n; i++) { res.append(ToString(vec[i])).append((i + 1 == n ? "" : ", ")); } res.append("]"); return res; } template <typename... Args> std::string ToString(const std::tuple<Args...>& tpl) { std::string res = "("; res.append(detail::ToStringImpl<std::tuple<Args...>, static_cast<int>(std::tuple_size_v<std::tuple<Args...>>) - 1>::Impl(tpl)); res.append(")"); return res; } template <typename K, typename D, typename A> std::string ToString(const std::set<K, D, A>& value) { return detail::ToStringSetImpl(value); } template <typename K, typename D, typename A> std::string ToString(const std::multiset<K, D, A>& value) { return detail::ToStringSetImpl(value); } template <typename K, typename H, typename D, typename A> std::string ToString(const std::unordered_set<K, H, D, A>& value) { return detail::ToStringSetImpl(value); } template <typename K, typename V, typename D, typename A> std::string ToString(const std::map<K, V, D, A>& value) { return detail::ToStringMapImpl(value); } template <typename K, typename V, typename D, typename A> std::string ToString(const std::multimap<K, V, D, A>& value) { return detail::ToStringMapImpl(value); } template <typename K, typename V, typename H, typename D, typename A> std::string ToString(const std::unordered_map<K, V, H, D, A>& value) { return detail::ToStringMapImpl(value); } template <typename... Args> std::string ToString(const std::pair<Args...>& tpl) { std::string res = "("; res.append(detail::ToStringImpl<std::pair<Args...>, 1>::Impl(tpl)); res.append(")"); return res; } std::string ToString(const std::string& value) { return value; } void Debug() { std::cerr << '\n'; } template <typename Head, typename... Tail> void Debug(const Head& head, const Tail&... tail) { std::cerr << " " << ToString(head); Debug(tail...); } } // namespace ext #ifdef local #define DEBUG(...) do { std::cerr << "[" << #__VA_ARGS__ << "]:"; ext::Debug(__VA_ARGS__); } while (0) #else #define DEBUG(...) do { } while (0) #endif int main() { std::cout << ext::ToString(std::vector<std::vector<int>>({{1, 2}, {2}})) << "\n"; std::cout << ext::ToString(std::make_tuple<int, int, std::string>(1, 2, "-")) << "\n"; std::cout << ext::ToString(std::make_tuple(1, std::make_tuple(2))) << '\n'; std::cout << ext::ToString(std::make_tuple()) << '\n'; std::cout << ext::ToString(std::map<int, std::tuple<int, int>> ({{1, {1, 1}}, {2, {-1, 1}}})) << '\n'; std::cout << ext::ToString(std::make_pair(1, std::make_tuple(1, 3))) << '\n'; std::cout << ext::ToString(std::unordered_set<int> ({1, 2, 3, 4})) << '\n'; DEBUG(1, std::make_pair(1, 2), std::make_pair(1, std::make_tuple(1, "3"))); }