Consider a Point
type with x
, y
and z
values. If I have a range of Point
objects, such as std::vector<Point>
, what do I need to add to Point
to make it work with the std::ranges::views::elements
range adaptor?
The intention is to do something like
std::vector<Point> v{...};
for (auto x : v | std::ranges::views::elements<0>) {
// do something with all `x` values
}
The documentation mentions that std::ranges::views::elements
works with "tuple-like" values. I have assumed that it should work similarly to how we can make our type work with structured binding, but I seem to be missing something
I have tried with the following code
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;
};
}
This is enough to make structured binding work, but std::ranges::views::elements
still does not work. Then I thought that maybe std::ranges::views::elements
needed std::get<n>(p)
to work and I added a specialization below to the std
namespace
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>();
}
Now it is possible to use std::get<0>(p) to extract the x
value, but again this is not enough for std::ranges::views::elements
. What else is necessary to make a range of Point
objects work with std::ranges::views::elements
?
PS: I know I could just use a views::transform
here, but my main intention here it to be generic and to understand how these things are suppose to fit together.generic and to understant how these things are supose to fit together.