给出以下 C++23 代码(Godbolt):
template<class R, class T>
concept container_compatible_range =
std::ranges::input_range<R> && std::convertible_to<std::ranges::range_reference_t<R>, T>;
template<class K, class V>
struct Map {
using value_type = std::pair<K, V>;
template<class R>
requires container_compatible_range<R, value_type>
void insert_range_bad(R&& rg) {
for (value_type e : rg) {
// ~~~~
}
}
template<class R>
requires container_compatible_range<R, value_type>
void insert_range_good(R&& rg) {
std::ranges::for_each(rg, [&](value_type e) {
// ~~~~
});
}
};
int main() {
Map<int, int> m;
std::tuple<int, int> a[2] = {}; // for example
m.insert_range_bad(a);
m.insert_range_good(a);
}
有人告诉我,存在一些“人为的极端情况”,在这些情况下,虽然insert_range_good
可以成功编译,但insert_range_bad
会失败(以一种不利于 SFINAE 的硬错误方式)和/或做错事。
这些极端情况是什么?
基于范围的循环以一致的
for
方式提取范围的迭代器和标记,这意味着两者都必须通过成员函数或自由函数获得。但对于 来说情况并非如此,因为两者都可以分别通过和以不同的方式
ranges::for_each
检索:ranges::begin
ranges::end
另一个人为的情况是删除
begin
/end
成员但启用免费begin
/end
功能;该类仍然满足range
概念,因为ranges::begin
/ranges::end
检查表达式的有效性,而基于范围的for
循环则不满足: