Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can can I make `std::ranges::views::elements` work with a range of my type

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.

like image 979
darcamo Avatar asked May 28 '21 15:05

darcamo


1 Answers

Can can I make std::ranges::views::elements work with a range of my type

No, you can't.

The way that elements is specified, in [range.elements.view], it's constrained on:

  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>&>;
    };

but we have to keep in mind the general rule in the library, from [contents]/3 that:

Whenever a name x defined in the standard library is mentioned, the name x is assumed to be fully qualified as​ ::​std​::​x, unless explicitly described otherwise. For example, if the Effects: element for library function F is described as calling library function G, the function​ ::​std​::​G is meant.

The get<N>(t) there is not an unqualified call to get, it's a call to ::std::get<N>(t) (there is no "unless explicitly described otherwise").

What that means is that this is testing std::get<0> (for keys), and that's not going to find user-provided structured bindings support (which should either be a member e.get<0>() or an unqualified get<0>(e) in the associated namespace). You can't just add overloads into std to make this work.

So... not currently supported.


Technically, if you stick your overload of std::get<N>(Point) into namespace std and ensure that it is defined before <ranges> is included, this will Just WorkTM. But this is highly fragile, both because you have to carefully control the include order (which you can't really do) and involves adding overloads into std (which you also shouldn't do either, especially in this case where those overloads wouldn't help structured bindings anyway).

like image 194
Barry Avatar answered Nov 20 '22 07:11

Barry