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.