【问题标题】:Printing a std::map using a std::range::copy and an adaptor使用 std::range::copy 和适配器打印 std::map
【发布时间】:2021-03-06 17:02:49
【问题描述】:

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

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

【问题讨论】:

  • 蓝图:定义一个Printable 概念。现在将pair_adaptor 更改为具有两个特化的通用模板,现有一个用于PrintablePair,一个用于Printable。写出来的代码太多了,但这是通用的方法。

标签: c++ templates constraints c++20 c++-concepts


【解决方案1】:

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

但是当您将std::pair&lt;int, std::pair&lt;int, int&gt;&gt; 作为pair_adaptor 的参数时,second_typestd::pair&lt;int, int&gt; 是不可打印的,除非我们将其转换为pair_adaptor&lt;std::pair&lt;int, int&gt;&gt;

另一种解决方案是我们可以先定义一个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)

【讨论】:

  • 我真的很喜欢这个解决方案的简单性。但是,如果 mapkeyvalue 不可打印,则它在运行时会失败。对于这种情况,您有解决方法吗?
  • @davidbea。如果 keymapvalue 中的任何一个无法打印,则它仍然在编译时失败。但是您可以只定义一个Printable 概念并在打印之前添加一个static_assert 检查。
  • 我注意到,当我使用 /std:c++latest 开关将编译器更改为 x86 MSVC v19.latest 时,if constexpr (requires {requires 行出现大量语法错误。您对我可以更改语法以使此代码在 MSVC 中工作有任何想法吗?它在 gcc 中完美运行,我非常喜欢添加 static_assert。
  • @davidbear。查看我的最新答案。
【解决方案2】:

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

也可以在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;
}

【讨论】:

  • 如果我理解正确......你可以用template &lt;class T&gt; concept NestedPrintablePair = PrintablePair&lt;T&gt; || ( (Printable&lt;typename T::first_type&gt; || PrintablePair&lt;typename T::first_type&gt;) &amp;&amp; (Printable&lt;typename T::second_type&gt; || PrintablePair&lt;typename T::second_type&gt;));简化一点
  • 或者template &lt;typename T, typename X1 = T::first_type, typename X2 = T::second_type&gt; concept NestedPrintablePair = PrintablePair&lt;T&gt; || ((Printable&lt;X1&gt; || PrintablePair&lt;X1&gt;) &amp;&amp; (Printable&lt;X2&gt; || PrintablePair&lt;X2&gt;));
  • 注意到当遇到更深的嵌套pair时,打印消息不可读。因为我们不知道1:2:3pair&lt;pair&lt;int, int&gt;, int&gt; 还是pair&lt;int, pair&lt;int, int&gt;&gt;
【解决方案3】:

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

问题是概念不能递归。

但是您可以基于可以递归的东西构造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;

当类型为std::pair&lt;T1, T2&gt; 并且T1T2 都是可打印的或可打印的对时,则为true 的递归模板变量

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>>;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-21
    • 1970-01-01
    • 2015-01-05
    • 2014-11-10
    • 2017-07-28
    相关资源
    最近更新 更多