如何打印 n 维 C++ STL 容器?容器是数组的数组或向量的向量
How to print a n-dimensional c++ STL container? Container is array of arrays or vectors of vectors
我有一个代码,我想在其中显示表示为向量的向量或 std::arrays of std::arrays 的张量。目的是按照 numpy 打印它们的方式打印它们。我仍在学习 C++ 中的元编程,并想探索如何使用函数模板打印 n-dim 容器,该函数模板可以接受这个容器容器并递归迭代它,return 一个我稍后可以计算的字符串。
Numpy 示例:
>>> np.ones([2,2])
array([[1., 1.],
[1., 1.]])
>>> np.ones([2,2,4])
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
>>> np.ones(4)
array([1., 1., 1., 1.])
>>>
目前已尝试:
我尝试了此处接受的响应作为响应:
Print simply STL vectors of vectors recursively in C++
它确实适用于 2 个暗向量,但是当我在 printContainerV2()
中将对 printContainer()
的调用更改为 printContainerV2()
时,使用 3d 向量编译失败:
代码:
#include <iostream>
#include <iterator>
#include <vector>
template <typename Iter, typename Cont>
bool isLast(Iter iter, const Cont& cont)
{
return (iter != cont.end()) && (next(iter) == cont.end());
}
template <typename T>
struct is_cont {
static const bool value = false;
};
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};
template <typename T>
std::string printContainer(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); ++ it)
if (isLast(it, container))
str = str + std::to_string(*it) + "}";
else
str = str + std::to_string(*it) + ",";
return str;
}
template<typename T>
using if_not_cont = std::enable_if<!is_cont<T>::value, T>;
template<typename T>
using if_cont = std::enable_if<is_cont<T>::value, T>;
template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); ++ it)
if (isLast(it, container))
str = str + std::to_string(*it) + "}";
else
str = str + std::to_string(*it) + ",";
return str;
}
template <typename T, typename std::enable_if<is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); ++ it)
if (isLast(it, container))
str = str + printContainerV2(*it) + "}";
else
str = str + printContainerV2(*it) + ",";
return str;
}
int main()
{
std::vector<int> A({2,3,6,8});
std::vector<std::vector<int>> M(2,A);
std::vector<std::vector<std::vector<float>>> m3{{{1,2}, {3,4}},{{5,6}, {7,8}},{{1,2}, {5,9}}};
std::cout << is_cont<decltype(A)>::value << std::endl; // returns true !
// for (auto it = std::begin(M); it != std::end(M); ++ it)
// {
// std::cout << printContainer(*it) << std::endl; // works well std::vector<int>
// std::cout << is_cont<decltype(*it)>::value << std::endl; // return false :(
// }
// Want to use this for printing a std::vector<std::vector<int>>
std::cout << printContainerV2(M) << std::endl; // not working !
std::cout << printContainerV2(m3) << std::endl; // not working
}
命令:clang++ --std=c++17 test.cpp
test.cpp是上面代码的名字。
我收到这个错误:
test.cpp:45:20: error: no matching function for call to 'begin'
for (auto it = std::begin(container); it != std::end(container); ++ it)
^~~~~~~~~~
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<int, nullptr>'
requested here
str = str + printContainerV2(*it) + "}";
^
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<std::__1::vector<int,
std::__1::allocator<int> >, nullptr>' requested here
test.cpp:80:19: note: in instantiation of function template specialization
'printContainerV2<std::__1::vector<std::__1::vector<int, std::__1::allocator<int> >,
std::__1::allocator<std::__1::vector<int, std::__1::allocator<int> > > >, nullptr>' requested here
std::cout << printContainerV2(M) << std::endl; // not working !
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/initializer_list:99:1: note:
candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against 'int'
begin(initializer_list<_Ep> __il) _NOEXCEPT
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1753:1: note:
candidate template ignored: could not match '_Tp [_Np]' against 'const int'
begin(_Tp (&__array)[_Np])
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1771:1: note:
candidate template ignored: substitution failure [with _Cp = const int]: member reference base type
'const int' is not a structure or union
begin(_Cp& __c) -> decltype(__c.begin())
^ ~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1779:1: note:
candidate template ignored: substitution failure [with _Cp = int]: member reference base type 'const int' is
not a structure or union
begin(const _Cp& __c) -> decltype(__c.begin())
^ ~
1 error generated.
这是打印任意维度向量的简单方法:
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T> &vec){
out << "[ ";
for(const auto& t: vec){
out << t << " ";
}
out << "] ";
return out;
}
对于任何其他容器,您可以执行完全相同的操作。
使用:
int main()
{
std::vector<int> v{1,2,3};
std::cout << v << std::endl;
std::vector<std::vector<std::vector<int>>> v_ds
{
{{{1,2,3},{4,5,6}},{{7,8},{9,10}}, {{1,2,3},{4,5,6}},{{7,8},{9,10}}}
};
std::cout << v_ds << std::endl;
return 0;
}
编辑: 这是 operator<< 的一个版本,它可以打印任何可以迭代的内容(例如 std::array、std::vector、std::list,甚至是已知边界的 c 数组):
template<typename Container, typename =
std::enable_if_t<std::is_same_v<std::void_t<
decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cbegin)),
decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cend))>, void>
&& !std::is_same_v<std::string, Container>>>
std::ostream& operator<<(std::ostream& out, const Container &vec)
{
out << "[ ";
for(const auto& t: vec){
out << t << " ";
}
out << "] ";
return out;
}
乱七八糟的,我知道,但是这样的语法糖有用:)
另一项编辑: 我之前发布的版本在使用 c 数组时略有错误。这是一个更新版本:
template<class...> constexpr bool true_t = true; // replacement of void_t
template<typename Container>
auto operator<<(std::ostream& out, Container&& vec) ->
std::enable_if_t<
true_t<
decltype(std::cbegin(vec)),
decltype(std::cend(vec))
>
&& !std::is_convertible_v<Container, std::string_view>,
std::ostream&
>
{
out << "[ ";
for(const auto& t: vec){
out << t << " ";
}
out << "] ";
return out;
}
当你的第一个printContainerV2
启用时,!is_cont<T>::value
是true
,这意味着此时你的T
不再是container
类型.在你的例子中,它们是 int
,所以你不能在 int
上调用 std::begin
,你应该直接 return std::to_string(value)
.
template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& value)
{
return std::to_string(value);
}
对于 C++17 中的嵌套容器,区分类型,你可以这样做:
#include <bits/stdc++.h>
using namespace std;
template <typename, typename = void> constexpr bool is_iterable{};
template <> constexpr bool is_iterable<string> = false;
template <typename T, size_t N> constexpr bool is_iterable<T[N]> = true;
template <typename T> constexpr bool is_iterable<T, void_t<decltype(declval<T>().begin()), decltype(declval<T>().end())>> = true;
template <typename> constexpr bool is_set{};
template <typename T> constexpr bool is_set<set<T>> = true;
template <typename T, typename U> constexpr bool is_set<map<T, U>> = true;
template <typename> constexpr bool is_tuple{};
template <typename... T> constexpr bool is_tuple<tuple<T...>> = true;
template <typename T, typename U> constexpr bool is_tuple<pair<T, U>> = true;
inline string to_string(string s) { return '"' + s + '"'; }
inline string to_string(char c) { return '\'' + string(1, c) + '\''; }
template<typename T>
string print(const T& t, string&& s = string(), size_t depth = 0) {
constexpr size_t TAB_SPACES = 2;
if constexpr (!is_tuple<T> and !is_iterable<T>) {
if (s.back() == '\n') s.pop_back();
s += to_string(t) + ", ";
} else {
pair<string, string> braces = is_tuple<T> ? pair("(", ")") : is_set<T> ? pair("{", "}") : pair("[", "]");
s += string(TAB_SPACES * depth, ' ') + braces.first + '\n';
if constexpr (is_tuple<T>)
if constexpr (tuple_size_v<T> == 0) s.push_back('[=10=]');
else apply([&s, depth](const auto&... x){ ((s = print(x, move(s), depth + 1)), ...); }, t);
else if (begin(t) == end(t)) s.push_back('[=10=]');
else for_each(begin(t), end(t), [&s, depth](const auto& x){ s = print(x, move(s), depth + 1); });
s.erase(s.length() - 2);
if (s.back() == ')' or s.back() == '}' or s.back() == ']') s += '\n' + string(TAB_SPACES * depth, ' ');
s += braces.second + ",\n";
}
if (depth == 0) cout << s.erase(s.length() - 2) << endl;
return move(s);
}
然后:
vector<map<int, string>> ex = {{{1, "a"}, {2, "b"}}};
print(ex);
将打印:
[
{
(1, "a"),
(2, "b")
}
]
用于脚本模板,其中优先考虑简单性和无外部依赖。生产可能由图书馆更好地服务。
我有一个代码,我想在其中显示表示为向量的向量或 std::arrays of std::arrays 的张量。目的是按照 numpy 打印它们的方式打印它们。我仍在学习 C++ 中的元编程,并想探索如何使用函数模板打印 n-dim 容器,该函数模板可以接受这个容器容器并递归迭代它,return 一个我稍后可以计算的字符串。
Numpy 示例:
>>> np.ones([2,2])
array([[1., 1.],
[1., 1.]])
>>> np.ones([2,2,4])
array([[[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[1., 1., 1., 1.],
[1., 1., 1., 1.]]])
>>> np.ones(4)
array([1., 1., 1., 1.])
>>>
目前已尝试: 我尝试了此处接受的响应作为响应:
Print simply STL vectors of vectors recursively in C++
它确实适用于 2 个暗向量,但是当我在 printContainerV2()
中将对 printContainer()
的调用更改为 printContainerV2()
时,使用 3d 向量编译失败:
代码:
#include <iostream>
#include <iterator>
#include <vector>
template <typename Iter, typename Cont>
bool isLast(Iter iter, const Cont& cont)
{
return (iter != cont.end()) && (next(iter) == cont.end());
}
template <typename T>
struct is_cont {
static const bool value = false;
};
template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
static const bool value = true;
};
template <typename T>
std::string printContainer(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); ++ it)
if (isLast(it, container))
str = str + std::to_string(*it) + "}";
else
str = str + std::to_string(*it) + ",";
return str;
}
template<typename T>
using if_not_cont = std::enable_if<!is_cont<T>::value, T>;
template<typename T>
using if_cont = std::enable_if<is_cont<T>::value, T>;
template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); ++ it)
if (isLast(it, container))
str = str + std::to_string(*it) + "}";
else
str = str + std::to_string(*it) + ",";
return str;
}
template <typename T, typename std::enable_if<is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& container)
{
std::string str = "{";
for (auto it = std::begin(container); it != std::end(container); ++ it)
if (isLast(it, container))
str = str + printContainerV2(*it) + "}";
else
str = str + printContainerV2(*it) + ",";
return str;
}
int main()
{
std::vector<int> A({2,3,6,8});
std::vector<std::vector<int>> M(2,A);
std::vector<std::vector<std::vector<float>>> m3{{{1,2}, {3,4}},{{5,6}, {7,8}},{{1,2}, {5,9}}};
std::cout << is_cont<decltype(A)>::value << std::endl; // returns true !
// for (auto it = std::begin(M); it != std::end(M); ++ it)
// {
// std::cout << printContainer(*it) << std::endl; // works well std::vector<int>
// std::cout << is_cont<decltype(*it)>::value << std::endl; // return false :(
// }
// Want to use this for printing a std::vector<std::vector<int>>
std::cout << printContainerV2(M) << std::endl; // not working !
std::cout << printContainerV2(m3) << std::endl; // not working
}
命令:clang++ --std=c++17 test.cpp
test.cpp是上面代码的名字。
我收到这个错误:
test.cpp:45:20: error: no matching function for call to 'begin'
for (auto it = std::begin(container); it != std::end(container); ++ it)
^~~~~~~~~~
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<int, nullptr>'
requested here
str = str + printContainerV2(*it) + "}";
^
test.cpp:59:29: note: in instantiation of function template specialization 'printContainerV2<std::__1::vector<int,
std::__1::allocator<int> >, nullptr>' requested here
test.cpp:80:19: note: in instantiation of function template specialization
'printContainerV2<std::__1::vector<std::__1::vector<int, std::__1::allocator<int> >,
std::__1::allocator<std::__1::vector<int, std::__1::allocator<int> > > >, nullptr>' requested here
std::cout << printContainerV2(M) << std::endl; // not working !
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/initializer_list:99:1: note:
candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against 'int'
begin(initializer_list<_Ep> __il) _NOEXCEPT
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1753:1: note:
candidate template ignored: could not match '_Tp [_Np]' against 'const int'
begin(_Tp (&__array)[_Np])
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1771:1: note:
candidate template ignored: substitution failure [with _Cp = const int]: member reference base type
'const int' is not a structure or union
begin(_Cp& __c) -> decltype(__c.begin())
^ ~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/iterator:1779:1: note:
candidate template ignored: substitution failure [with _Cp = int]: member reference base type 'const int' is
not a structure or union
begin(const _Cp& __c) -> decltype(__c.begin())
^ ~
1 error generated.
这是打印任意维度向量的简单方法:
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T> &vec){
out << "[ ";
for(const auto& t: vec){
out << t << " ";
}
out << "] ";
return out;
}
对于任何其他容器,您可以执行完全相同的操作。
使用:
int main()
{
std::vector<int> v{1,2,3};
std::cout << v << std::endl;
std::vector<std::vector<std::vector<int>>> v_ds
{
{{{1,2,3},{4,5,6}},{{7,8},{9,10}}, {{1,2,3},{4,5,6}},{{7,8},{9,10}}}
};
std::cout << v_ds << std::endl;
return 0;
}
编辑: 这是 operator<< 的一个版本,它可以打印任何可以迭代的内容(例如 std::array、std::vector、std::list,甚至是已知边界的 c 数组):
template<typename Container, typename =
std::enable_if_t<std::is_same_v<std::void_t<
decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cbegin)),
decltype(static_cast<typename Container::const_iterator (*)(const Container&)>(&std::cend))>, void>
&& !std::is_same_v<std::string, Container>>>
std::ostream& operator<<(std::ostream& out, const Container &vec)
{
out << "[ ";
for(const auto& t: vec){
out << t << " ";
}
out << "] ";
return out;
}
乱七八糟的,我知道,但是这样的语法糖有用:)
另一项编辑: 我之前发布的版本在使用 c 数组时略有错误。这是一个更新版本:
template<class...> constexpr bool true_t = true; // replacement of void_t
template<typename Container>
auto operator<<(std::ostream& out, Container&& vec) ->
std::enable_if_t<
true_t<
decltype(std::cbegin(vec)),
decltype(std::cend(vec))
>
&& !std::is_convertible_v<Container, std::string_view>,
std::ostream&
>
{
out << "[ ";
for(const auto& t: vec){
out << t << " ";
}
out << "] ";
return out;
}
当你的第一个printContainerV2
启用时,!is_cont<T>::value
是true
,这意味着此时你的T
不再是container
类型.在你的例子中,它们是 int
,所以你不能在 int
上调用 std::begin
,你应该直接 return std::to_string(value)
.
template <typename T, typename std::enable_if<!is_cont<T>::value, T>::type* = nullptr>
std::string printContainerV2(T const& value)
{
return std::to_string(value);
}
对于 C++17 中的嵌套容器,区分类型,你可以这样做:
#include <bits/stdc++.h>
using namespace std;
template <typename, typename = void> constexpr bool is_iterable{};
template <> constexpr bool is_iterable<string> = false;
template <typename T, size_t N> constexpr bool is_iterable<T[N]> = true;
template <typename T> constexpr bool is_iterable<T, void_t<decltype(declval<T>().begin()), decltype(declval<T>().end())>> = true;
template <typename> constexpr bool is_set{};
template <typename T> constexpr bool is_set<set<T>> = true;
template <typename T, typename U> constexpr bool is_set<map<T, U>> = true;
template <typename> constexpr bool is_tuple{};
template <typename... T> constexpr bool is_tuple<tuple<T...>> = true;
template <typename T, typename U> constexpr bool is_tuple<pair<T, U>> = true;
inline string to_string(string s) { return '"' + s + '"'; }
inline string to_string(char c) { return '\'' + string(1, c) + '\''; }
template<typename T>
string print(const T& t, string&& s = string(), size_t depth = 0) {
constexpr size_t TAB_SPACES = 2;
if constexpr (!is_tuple<T> and !is_iterable<T>) {
if (s.back() == '\n') s.pop_back();
s += to_string(t) + ", ";
} else {
pair<string, string> braces = is_tuple<T> ? pair("(", ")") : is_set<T> ? pair("{", "}") : pair("[", "]");
s += string(TAB_SPACES * depth, ' ') + braces.first + '\n';
if constexpr (is_tuple<T>)
if constexpr (tuple_size_v<T> == 0) s.push_back('[=10=]');
else apply([&s, depth](const auto&... x){ ((s = print(x, move(s), depth + 1)), ...); }, t);
else if (begin(t) == end(t)) s.push_back('[=10=]');
else for_each(begin(t), end(t), [&s, depth](const auto& x){ s = print(x, move(s), depth + 1); });
s.erase(s.length() - 2);
if (s.back() == ')' or s.back() == '}' or s.back() == ']') s += '\n' + string(TAB_SPACES * depth, ' ');
s += braces.second + ",\n";
}
if (depth == 0) cout << s.erase(s.length() - 2) << endl;
return move(s);
}
然后:
vector<map<int, string>> ex = {{{1, "a"}, {2, "b"}}};
print(ex);
将打印:
[
{
(1, "a"),
(2, "b")
}
]
用于脚本模板,其中优先考虑简单性和无外部依赖。生产可能由图书馆更好地服务。