我可以使 `std::ranges::views::elements` 与我的类型范围一起工作吗
Can can I make `std::ranges::views::elements` work with a range of my type
考虑具有 x
、y
和 z
值的 Point
类型。如果我有一系列 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 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 ,不过我好像少了点什么
我试过下面的代码
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<0>(p) 来提取 x
值,但是对于 std::ranges::views::elements
这还是不够的。要使一系列 Point
对象与 std::ranges::views::elements
一起工作还需要什么?
PS:我知道我可以在这里使用 views::transform
,但我在这里的主要目的是通用并了解这些东西应该如何适合 together.generic 和了解这些东西应该如何组合在一起。
目的是在您的 命名空间(用于 ADL)中提供非成员 get
函数模板。这 比结构化绑定更严格,结构化绑定也支持成员 get
。
您的专业化无效,因为它不是专业化:它是一个 重载 ,因此在命名空间 std
中是不允许的(实际上没有被找到从 <ranges>
).
中查找名称
Can can I make std::ranges::views::elements
work with a range of my type
不,你不能†.
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 的是:
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.
get<N>(t)
没有对 get
的不合格调用,它是对 ::std::get<N>(t)
的调用(没有“除非另有明确说明”)。
这意味着这是在测试 std::get<0>
(对于 keys
),并且不会找到用户提供的结构化绑定支持(应该是成员 e.get<0>()
或关联命名空间中的非限定 get<0>(e)
)。您不能只在 std
中添加重载来完成这项工作。
所以...目前不支持。
†从技术上讲,如果您将 std::get<N>(Point)
的重载粘贴到命名空间 std
中并确保它在 之前被定义 <ranges>
包括在内,这将 Just WorkTM。但这是非常脆弱的,因为你必须仔细控制包含顺序(你不能真正做到)并且涉及向 std
添加重载(你也不应该这样做,特别是在这种情况下无论如何,这些重载对结构化绑定没有帮助。
考虑具有 x
、y
和 z
值的 Point
类型。如果我有一系列 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 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
我试过下面的代码
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<0>(p) 来提取 x
值,但是对于 std::ranges::views::elements
这还是不够的。要使一系列 Point
对象与 std::ranges::views::elements
一起工作还需要什么?
PS:我知道我可以在这里使用 views::transform
,但我在这里的主要目的是通用并了解这些东西应该如何适合 together.generic 和了解这些东西应该如何组合在一起。
目的是在您的 命名空间(用于 ADL)中提供非成员 get
函数模板。这 比结构化绑定更严格,结构化绑定也支持成员 get
。
您的专业化无效,因为它不是专业化:它是一个 重载 ,因此在命名空间 std
中是不允许的(实际上没有被找到从 <ranges>
).
Can can I make
std::ranges::views::elements
work with a range of my type
不,你不能†.
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 的是:
Whenever a name
x
defined in the standard library is mentioned, the namex
is assumed to be fully qualified as::std::
x, unless explicitly described otherwise. For example, if the Effects: element for library functionF
is described as calling library functionG
, the function::std::G
is meant.
get<N>(t)
没有对 get
的不合格调用,它是对 ::std::get<N>(t)
的调用(没有“除非另有明确说明”)。
这意味着这是在测试 std::get<0>
(对于 keys
),并且不会找到用户提供的结构化绑定支持(应该是成员 e.get<0>()
或关联命名空间中的非限定 get<0>(e)
)。您不能只在 std
中添加重载来完成这项工作。
所以...目前不支持。
†从技术上讲,如果您将 std::get<N>(Point)
的重载粘贴到命名空间 std
中并确保它在 之前被定义 <ranges>
包括在内,这将 Just WorkTM。但这是非常脆弱的,因为你必须仔细控制包含顺序(你不能真正做到)并且涉及向 std
添加重载(你也不应该这样做,特别是在这种情况下无论如何,这些重载对结构化绑定没有帮助。