如何解决我可以让 `std::ranges::views::elements` 处理我的类型范围
考虑具有 Point
、x
和 y
值的 z
类型。如果我有一系列 Point
对象,例如 std::vector<Point>
,我需要向 Point
添加什么才能使其与 std::ranges::views::elements
范围适配器一起使用?
目的是做类似的事情
std::vector<Point> v{...};
for (auto x : v | std::ranges::views::elements<0>) {
// do something with all `x` values
}
documentation 提到 std::ranges::views::elements
可以处理“类似元组”的值。我假设它应该与我们如何使我们的类型 work with structured binding 类似,但我似乎遗漏了一些东西
我已尝试使用以下代码
class Point {
double x=0;
double y=0;
double z=0;
public:
Point(double x,double y,double z) : x(x),y(y),z(z) {}
template <std::size_t N>
double get() const {
if constexpr(N == 0)
return x;
else if constexpr(N == 1)
return y;
else if constexpr(N == 2)
return z;
}
};
namespace std {
template <>
struct tuple_size<Point> : std::integral_constant<std::size_t,3> {};
template <std::size_t N>
struct tuple_element<N,Point> {
using type = double;
};
}
这足以使结构化绑定工作,但 std::ranges::views::elements
仍然不起作用。然后我想也许 std::ranges::views::elements
需要 std::get<n>(p)
才能工作,于是我在 std
命名空间
template <std::size_t N>
double get(const Point &p) {
if constexpr(N == 0)
return p.get<0>();
else if constexpr(N==1)
return p.get<1>();
else if constexpr(N==2)
return p.get<2>();
}
现在可以使用 std::get(p) 来提取 x
值,但这对于 std::ranges::views::elements
来说还是不够的。使一系列 Point
对象与 std::ranges::views::elements
一起使用还需要什么?
PS:我知道我可以在这里只使用 views::transform
,但我在这里的主要意图是泛型并了解这些东西是如何组合在一起的。泛型并了解这些东西是如何适应的结合在一起。
解决方法
我可以让 std::ranges::views::elements
在我的类型范围内工作吗
不,你不能†。
指定 elements
的方式,在 [range.elements.view] 中,它被限制在:
template<class T,size_t N>
concept has-tuple-element = // exposition only
requires(T t) {
typename tuple_size<T>::type;
requires N < tuple_size_v<T>;
typename tuple_element_t<N,T>;
{ get<N>(t) } -> convertible_to<const tuple_element_t<N,T>&>;
};
但我们必须牢记库中的一般规则,从 [contents]/3 开始:
每当提到标准库中定义的名称 x
时,除非另有明确说明,否则假定名称 x
完全限定为 ::std::
x。例如,如果库函数 F
的 Effects: 元素被描述为调用库函数 G
,则表示函数::std::G
。
get<N>(t)
不是对 get
的不合格调用,而是对 ::std::get<N>(t)
的调用(没有“除非另有明确说明”)。
这意味着这是在测试 std::get<0>
(对于 keys
),并且不会找到用户提供的结构化绑定支持(应该是成员 e.get<0>()
或关联命名空间中的非限定 get<0>(e)
)。您不能仅在 std
中添加重载来使其工作。
所以...目前不支持。
†从技术上讲,如果您将 std::get<N>(Point)
的重载放入命名空间 std
并确保它在 before <ranges>
包括在内,这将只是工作TM。但这非常脆弱,因为您必须仔细控制包含顺序(您实际上无法做到)并且涉及向 std
添加重载(您也不应该这样做,尤其是在这种情况下无论如何,这些重载对结构化绑定没有帮助)。
目的是在您的命名空间(用于 ADL)中提供非成员 get
函数模板。这比结构化绑定更严格,后者也支持成员 get
。
您的专业化不起作用,因为它不是一个专业化:它是一个重载,因此在命名空间 std
中是不允许的(实际上在 <ranges>
中没有通过名称查找找到) {1}})。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。