【问题标题】:std::ranges::find_if - no type in std::common_referencestd::ranges::find_if - std::common_reference 中没有类型
【发布时间】:2022-02-17 00:17:16
【问题描述】:

我使用 SG14 flat_map 作为容器。

按照标准映射,它采用KeyValue 模板参数。

然而,与标准映射不同,它不会将std::pair<Key, Value> 存储在二叉搜索树中,而是将键和值存储在两个单独的容器中(附加的模板参数默认为std::vector

template<
    class Key,
    class Mapped,
    class Compare = std::less<Key>,
    class KeyContainer = std::vector<Key>,
    class MappedContainer = std::vector<Mapped>
>
class flat_map

然后它定义了一些类型如下:

using key_type = Key;
using mapped_type = Mapped;
using value_type = std::pair<const Key, Mapped>;
using key_compare = Compare;
using const_key_reference = typename KeyContainer::const_reference;
using mapped_reference = typename MappedContainer::reference;
using const_mapped_reference = typename MappedContainer::const_reference;
using reference = std::pair<const_key_reference, mapped_reference>;
using const_reference = std::pair<const_key_reference, const_mapped_reference>;

如果我尝试在flat_map 上使用std::ranges::find_if,则会收到错误消息:

error: no type named ‘type’ in 
    ‘struct std::common_reference<std::pair<const Key&, const Value&>&&, 
                                  std::pair<const Key, Value>&>’
  121 | auto it = std::ranges::find_if(map, [](auto& kv) { return kv.second.name == "foo"; });
      |           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如果我使用非范围 find_if,一切都“正常工作”

auto it = std::find_if(map.begin(), map.end(), [](auto& kv) { return kv.second.name == "foo"; });

为什么std::ranges::find_if 不起作用?

godbolt 示例:https://godbolt.org/z/r93f7qozr

编辑:

@Brian 提供了一个成功编译的示例 - 尽管与我的略有不同 - 特别是我的地图是 const,我将 lambda 参数作为 const ref...

这引出了问题:

  • 为什么 const range 和 const auto&amp; lambda 参数的组合无法编译,而传递可变范围有效并按值获取 lambda 参数有效?
  • 我相信将非范围 std::find_if 算法的 lambda 参数按值(auto 而不是 const auto&amp;)会被认为是一种反模式,因为这会导致每个元素都被复制 -因此应该首选使用const auto&amp;... 最小意外原则意味着我认为std::ranges 的情况也是如此 - 不是这样吗?

【问题讨论】:

  • 1) 请提供minimal reproducible example 2) can't reproducestdext::flat_map 似乎可以在 GCC 和 clang 中继上与 std::ranges::find_if 一起正常工作。
  • 你也可以看看可能的实现here。也许将它复制到一个新文件上,这样您就可以四处看看问题是否是flat_map 缺少某些东西(别名成员、operator* 等成员函数等)。
  • @Brian 请在此处找到示例:godbolt.org/z/r93f7qozr(我已经更新了问题以包含链接)...特别是我和你的区别在于地图是 const,而我将 lambda 参数作为 const ref
  • @SteveLorimer 太好了,很高兴我们缩小了范围!可能值得注意的是,non-const references and a mutable map 会出现同样的错误,而const references and a mutable map 会出现
  • 注意,我会把它写成auto it1 = std::ranges::find(map, 0, &amp;decltype(map)::value_type::second);,但无论如何,这也行不通。你得到了很多没有满足的要求,比如 "no type named 'type' in 'struct std::common_reference<:pair int const>&&, std::pair&>'"

标签: c++ c++20 std-ranges


【解决方案1】:

为什么const range 和 const auto&amp; lambda 的组合 参数无法编译,而传递可变范围有效并且 按值获取 lambda 参数有效吗?

首先,flat_map的迭代器的operator*()定义如下:

reference operator*() const {
  return reference{*kit_, *vit_};
}

reference的类型是pair,也就是说operator*()会返回一个pairprvalue,所以lambda的参数类型不能是auto&amp;,也就是左值引用,因为它不能绑定右值。

第二,const flat_map没有建模input_range的概念,即它的迭代器没有建模需要input_iteratorindirectly_readable需要common_reference_with&lt;iter_reference_t&lt;In&gt;&amp;&amp;, iter_value_t&lt;In&gt;&amp;&gt;,前者是pair&lt;const int&amp;, const int&amp;&gt;&amp;&amp;,后者是pair&lt;const int, int&gt;&amp;,两者都没有common_reference

解决方法是为它们定义 common_reference,就像 P2321 所做的那样(这也意味着您的代码在 C++23 中格式正确):

template<class T1, class T2, class U1, class U2,
         template<class> class TQual, template<class> class UQual>
  requires requires { typename pair<common_reference_t<TQual<T1>, UQual<U1>>,
                                    common_reference_t<TQual<T2>, UQual<U2>>>; }
struct basic_common_reference<pair<T1, T2>, pair<U1, U2>, TQual, UQual> {
  using type = pair<common_reference_t<TQual<T1>, UQual<U1>>,
                    common_reference_t<TQual<T2>, UQual<U2>>>;
};

common_reference详情可以参考this question

【讨论】:

    猜你喜欢
    • 2023-03-20
    • 2020-01-11
    • 2020-09-22
    • 2021-10-01
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多