std::ranges::find_if - std::common_reference 中没有类型
std::ranges::find_if - no type in std::common_reference
我正在使用 SG14 flat_map
作为容器。
根据标准地图,它需要 Key
和 Value
模板参数。
然而,与标准映射不同的是,它不将 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
...
这引出了问题:
- 为什么
const
范围和 const auto&
lambda 参数的组合无法编译,而传递可变范围有效并按值获取 lambda 参数有效?
- 我认为按值(
auto
而不是 const auto&
)采用非范围 std::find_if
算法的 lambda 参数会被认为是一种反模式将导致每个元素都被复制 - 因此应该首选使用 const auto&
...最小意外原则意味着我假设 std::ranges
也是如此 - 不是这样吗?
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一个prvalue的pair
,所以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
的详细内容可以参考.
我正在使用 SG14 flat_map
作为容器。
根据标准地图,它需要 Key
和 Value
模板参数。
然而,与标准映射不同的是,它不将 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
...
这引出了问题:
- 为什么
const
范围和const auto&
lambda 参数的组合无法编译,而传递可变范围有效并按值获取 lambda 参数有效? - 我认为按值(
auto
而不是const auto&
)采用非范围std::find_if
算法的 lambda 参数会被认为是一种反模式将导致每个元素都被复制 - 因此应该首选使用const auto&
...最小意外原则意味着我假设std::ranges
也是如此 - 不是这样吗?
Why does the combination of
const
range andconst 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一个prvalue的pair
,所以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
的详细内容可以参考