使用 std::range::copy 和适配器打印 std::map
Printing a std::map using a std::range::copy and an adaptor
仍在努力学习如何使用模板、概念和约束。
我想用std::ranges::copy()
打印出一个std::map
的内容,结果看到了这个answer。印象深刻,我想知道我是否可以将 pair_adaptor
限制为仅在元素可打印的 std::pairs
上工作。
所以,我写了这个:
template <class T>
concept PrintablePair = requires(std::ostream & os, T a)
{
os << a.first;
os << a.second;
};
template <PrintablePair pair_type>
class pair_adaptor
{
public:
const pair_type& m;
pair_adaptor(const pair_type& a) : m(a) {}
friend std::ostream& operator << (std::ostream& out,
const pair_adaptor <pair_type>& d)
{
const pair_type& m = d.m;
return out << m.first << " => " << m.second;
}
};
这适用于看起来像这样的地图:
std::map<int,int> m1;
std::ranges::copy(m1,
std::ostream_iterator<
pair_adaptor<decltype(*m1.begin())> >(std::cout, "\n"));
现在,我想进一步扩展它,以便我可以打印如下所示的地图:
std::map<int, std::pair<int, int>>
所以,我认为我需要能够递归打印对,但我迷路了。
So, I think that I need to be able to recursively print pairs, but I am lost.
问题是概念不能递归。
但是你可以根据一些可以递归的东西构造一个concept
,例如:模板变量。
您可以定义一个模板变量来说明类型是否可打印
constexpr std::false_type print_test (...);
template <typename T>
constexpr auto print_test (T t)
-> decltype( std::declval<std::ostream>() << t, std::true_type{});
template <typename T>
constexpr bool isPrint = decltype(print_test(std::declval<T>()))::value;
然后递归模板变量是 true
当类型是 std::pair<T1, T2>
并且 T1
和 T2
都是可打印的或可打印的对
template <typename>
constexpr bool isPrintPair { false };
template <typename T1, typename T2>
constexpr bool isPrintPair<std::pair<T1, T2>>
{ (isPrint<T1> || isPrintPair<T1>) && (isPrint<T2> || isPrintPair<T2>) };
现在你的 concept
变成了
template <typename T>
concept PrintablePair = isPrintPair<std::decay_t<T>>;
至于 C++ 概念不能递归,您必须为其定义上层概念 NestedPrintablePair
和 operator<<
。
另见直播 https://wandbox.org/permlink/pe7GzkFrEvxXoKmu:
#include <algorithm>
#include <iostream>
#include <map>
#include <ranges>
template <class T>
concept Printable = requires(std::ostream & os, T a)
{
os << a;
};
template <class T>
concept PrintablePair = Printable<typename T::first_type> &&
Printable<typename T::second_type>;
template <typename T, typename X1 = T::first_type, typename X2 = T::second_type>
concept NestedPrintablePair = PrintablePair<T> ||
((Printable<X1> || PrintablePair<X1>) && (Printable<X2> || PrintablePair<X2>));
template <PrintablePair T>
std::ostream& operator << (std::ostream& out, const T& p)
{
return out << p.first << ":" << p.second;
}
template <NestedPrintablePair pair_type>
class pair_adaptor
{
public:
const pair_type& m;
pair_adaptor(const pair_type& a) : m(a) {}
friend std::ostream& operator << (std::ostream& out,
const pair_adaptor<pair_type>& d)
{
const pair_type& m = d.m;
return out << m.first << " => " << m.second;
}
};
int main()
{
std::map<int,int> m1 { {1,2}, {3,4} };
std::ranges::copy(m1,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m1.begin())>::type > >(std::cout, "\n"));
std::map<int, std::pair<int, int>> m2 { {1, {2, 3} }, {3, {4, 5} } };
std::ranges::copy(m2,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m2.begin())>::type > >(std::cout, "\n"));
std::map<std::pair<int, int>, int> m3 { { {2, 3}, 1}, { {4, 5}, 6} };
std::ranges::copy(m3,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m3.begin())>::type > >(std::cout, "\n"));
std::map<std::pair<int, int>, std::pair<int, int>> m4
{ { {1, 2}, {3, 4}}, { {4, 5}, {6, 7} } };
std::ranges::copy(m4,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m4.begin())>::type > >(std::cout, "\n"));
return 0;
}
问题是你的 PrintablePair
概念 requires
类型 T
的 first_type
和 second_type
应该是可打印的,而你的 pair_adaptor
模板参数必须满足PrintablePair
概念。
但是当你将std::pair<int, std::pair<int, int>>
作为pair_adaptor
的参数时,second_type
即std::pair<int, int>
是不可打印的,除非我们将其转换为pair_adaptor<std::pair<int, int>>
.
替代方案是我们可以先定义一个std::pair
概念:
template <class P>
concept Pair = std::same_as<std::pair<
typename P::first_type,
typename P::second_type>,
P>;
那我们遇到一个std::pair
,就改成pair_adaptor
:
template <class T>
concept Printable = requires(std::ostream& os, T a) { os << a; };
template <Pair pair_type>
class pair_adaptor {
public:
const pair_type& m;
pair_adaptor(const pair_type& a) : m(a) {}
friend std::ostream& operator<<(std::ostream& out, const pair_adaptor& d) {
auto print = [&out]<typename T>(const T& x) {
if constexpr (Pair<T>) out << pair_adaptor<T>{x};
else {
static_assert(Printable<T>);
out << x;
}
};
const pair_type& m = d.m;
out << "(";
print(m.first);
out << " => ";
print(m.second);
out << ")";
return out;
}
};
仍在努力学习如何使用模板、概念和约束。
我想用std::ranges::copy()
打印出一个std::map
的内容,结果看到了这个answer。印象深刻,我想知道我是否可以将 pair_adaptor
限制为仅在元素可打印的 std::pairs
上工作。
所以,我写了这个:
template <class T>
concept PrintablePair = requires(std::ostream & os, T a)
{
os << a.first;
os << a.second;
};
template <PrintablePair pair_type>
class pair_adaptor
{
public:
const pair_type& m;
pair_adaptor(const pair_type& a) : m(a) {}
friend std::ostream& operator << (std::ostream& out,
const pair_adaptor <pair_type>& d)
{
const pair_type& m = d.m;
return out << m.first << " => " << m.second;
}
};
这适用于看起来像这样的地图:
std::map<int,int> m1;
std::ranges::copy(m1,
std::ostream_iterator<
pair_adaptor<decltype(*m1.begin())> >(std::cout, "\n"));
现在,我想进一步扩展它,以便我可以打印如下所示的地图:
std::map<int, std::pair<int, int>>
所以,我认为我需要能够递归打印对,但我迷路了。
So, I think that I need to be able to recursively print pairs, but I am lost.
问题是概念不能递归。
但是你可以根据一些可以递归的东西构造一个concept
,例如:模板变量。
您可以定义一个模板变量来说明类型是否可打印
constexpr std::false_type print_test (...);
template <typename T>
constexpr auto print_test (T t)
-> decltype( std::declval<std::ostream>() << t, std::true_type{});
template <typename T>
constexpr bool isPrint = decltype(print_test(std::declval<T>()))::value;
然后递归模板变量是 true
当类型是 std::pair<T1, T2>
并且 T1
和 T2
都是可打印的或可打印的对
template <typename>
constexpr bool isPrintPair { false };
template <typename T1, typename T2>
constexpr bool isPrintPair<std::pair<T1, T2>>
{ (isPrint<T1> || isPrintPair<T1>) && (isPrint<T2> || isPrintPair<T2>) };
现在你的 concept
变成了
template <typename T>
concept PrintablePair = isPrintPair<std::decay_t<T>>;
至于 C++ 概念不能递归,您必须为其定义上层概念 NestedPrintablePair
和 operator<<
。
另见直播 https://wandbox.org/permlink/pe7GzkFrEvxXoKmu:
#include <algorithm>
#include <iostream>
#include <map>
#include <ranges>
template <class T>
concept Printable = requires(std::ostream & os, T a)
{
os << a;
};
template <class T>
concept PrintablePair = Printable<typename T::first_type> &&
Printable<typename T::second_type>;
template <typename T, typename X1 = T::first_type, typename X2 = T::second_type>
concept NestedPrintablePair = PrintablePair<T> ||
((Printable<X1> || PrintablePair<X1>) && (Printable<X2> || PrintablePair<X2>));
template <PrintablePair T>
std::ostream& operator << (std::ostream& out, const T& p)
{
return out << p.first << ":" << p.second;
}
template <NestedPrintablePair pair_type>
class pair_adaptor
{
public:
const pair_type& m;
pair_adaptor(const pair_type& a) : m(a) {}
friend std::ostream& operator << (std::ostream& out,
const pair_adaptor<pair_type>& d)
{
const pair_type& m = d.m;
return out << m.first << " => " << m.second;
}
};
int main()
{
std::map<int,int> m1 { {1,2}, {3,4} };
std::ranges::copy(m1,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m1.begin())>::type > >(std::cout, "\n"));
std::map<int, std::pair<int, int>> m2 { {1, {2, 3} }, {3, {4, 5} } };
std::ranges::copy(m2,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m2.begin())>::type > >(std::cout, "\n"));
std::map<std::pair<int, int>, int> m3 { { {2, 3}, 1}, { {4, 5}, 6} };
std::ranges::copy(m3,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m3.begin())>::type > >(std::cout, "\n"));
std::map<std::pair<int, int>, std::pair<int, int>> m4
{ { {1, 2}, {3, 4}}, { {4, 5}, {6, 7} } };
std::ranges::copy(m4,
std::ostream_iterator<
pair_adaptor<std::decay<decltype(*m4.begin())>::type > >(std::cout, "\n"));
return 0;
}
问题是你的 PrintablePair
概念 requires
类型 T
的 first_type
和 second_type
应该是可打印的,而你的 pair_adaptor
模板参数必须满足PrintablePair
概念。
但是当你将std::pair<int, std::pair<int, int>>
作为pair_adaptor
的参数时,second_type
即std::pair<int, int>
是不可打印的,除非我们将其转换为pair_adaptor<std::pair<int, int>>
.
替代方案是我们可以先定义一个std::pair
概念:
template <class P>
concept Pair = std::same_as<std::pair<
typename P::first_type,
typename P::second_type>,
P>;
那我们遇到一个std::pair
,就改成pair_adaptor
:
template <class T>
concept Printable = requires(std::ostream& os, T a) { os << a; };
template <Pair pair_type>
class pair_adaptor {
public:
const pair_type& m;
pair_adaptor(const pair_type& a) : m(a) {}
friend std::ostream& operator<<(std::ostream& out, const pair_adaptor& d) {
auto print = [&out]<typename T>(const T& x) {
if constexpr (Pair<T>) out << pair_adaptor<T>{x};
else {
static_assert(Printable<T>);
out << x;
}
};
const pair_type& m = d.m;
out << "(";
print(m.first);
out << " => ";
print(m.second);
out << ")";
return out;
}
};