微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我可以让 `std::ranges::views::elements` 处理我的类型范围

如何解决我可以让 `std::ranges::views::elements` 处理我的类型范围

考虑具有 Pointxy 值的 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。例如,如果库函数 FEffects: 元素被描述为调用库函数 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 举报,一经查实,本站将立刻删除。