std::ranges::find_if - std::common_reference 中没有类型

std::ranges::find_if - no type in std::common_reference

我正在使用 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 不起作用?

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

编辑:

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

这引出了问题:

Why does the combination of const range and const auto& lambda argument fail to compile, while pasing a mutable range works and taking the lambda argument by value works?

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

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

reference的类型是pair,这意味着operator*()将return一个prvaluepair,所以lambda的参数类型不能是auto&,即左值引用,因为不能绑定右值

其次,const flat_map没有建模需要common_reference_with<iter_reference_t<In>&&, iter_value_t<In>&>input_range concept, that is, its iterator does not model input_iterator which requires indirectly_readable,前者是pair<const int&, const int&>&&,后者是pair<const int, int>&,有没有common_reference两个。

解决方法是为它们定义 common_reference,就像 P2321 那样(这也意味着您的代码在 C++23 中是 well-formed):

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的详细内容可以参考.