微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 std::range::copy 和适配器打印 std::map

如何解决使用 std::range::copy 和适配器打印 std::map

仍在努力学习如何使用模板、概念、约束。 我想用 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>>

所以,我认为我需要能够递归打印对,但我迷路了。

解决方法

至于 C++ 概念不能递归,您必须为其定义上层概念 NestedPrintablePairoperator<<

另见 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} },{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>> m4 
        { { {1,4}},{6,7} } };
    
    std::ranges::copy(m4,std::ostream_iterator<
        pair_adaptor<std::decay<decltype(*m4.begin())>::type > >(std::cout,"\n"));
    
    return 0;
}
,

问题是您的 PrintablePair 概念 requires 类型 Tfirst_typesecond_type 应该是可打印的,并且您的 pair_adaptor 模板参数必须满足 PrintablePair 概念。

但是当您将 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;
  }
};

直播demo (Examples steal from @Rost)

,

所以,我认为我需要能够递归打印对,但我迷路了。

问题是概念不能递归。

但是您可以基于可以递归的内容构建 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>>;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。