C++20 keys_view 和 values_view 的真正目的是什么?
The real purpose of C++20 keys_view and values_view?
C++20 引入了 ranges::elements_view
,它接受 view
的 类似元组的 值,并发出一个 view
值类型 改编后的 view
值类型的第 N 个元素,其中 N 是非类型模板参数。
在[range.elements.view]中ranges::elements_view
的概要定义为:
template<input_range V, size_t N>
requires view<V> && has-tuple-element<range_value_t<V>, N> &&
has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
returnable-element<range_reference_t<V>, N>
class elements_view : public view_interface<elements_view<V, N>> {
public:
elements_view() = default;
constexpr explicit elements_view(V base);
// ...
};
由于elements_view
的构造函数只包含参数V
,所以不指定所有模板参数(godbolt)是无法直接初始化elements_view
的:
std::array<std::tuple<int, float>, 5> r{};
ranges::elements_view<0>{r}; // error: wrong number of template arguments (1, should be 2)
ranges::elements_view<decltype(views::all(r)), 0>{r}; // ok
views::elements<0>(r); // ok
这使得它失去了其他范围适配器的表达式等价性,例如 ranges::transform_view
:
ranges::transform_view{r, [](auto elem) { return std::get<0>(elem); }}; // ok
views::transform(r, [](auto elem) { return std::get<0>(elem); }); // ok
并且在elements_view
的基础上,标准新增了keys_view
和values_view
,它们都是elements_view<views::all_t<R>, N>
的别名:
template <class R>
using keys_view = elements_view<views::all_t<R>, 0>;
template <class R>
using values_view = elements_view<views::all_t<R>, 1>;
该标准在 [range.elements#overview-example-2]:
中给出了它的用例
auto historical_figures = map{
{"Lovelace"sv, 1815},
{"Turing"sv, 1912},
{"Babbage"sv, 1791},
{"Hamilton"sv, 1936}
};
auto names = keys_view{historical_figures};
for (auto&& name : names) {
cout << name << ' '; // prints Babbage Hamilton Lovelace Turing
}
也就是not quite right,因为我们无法推导出R
的类型,所以构造keys_view
的唯一方法就是指定所有模板参数,就像elements_view
一样:
auto names = std::ranges::keys_view<decltype(std::views::all(historical_figures))>{historical_figures};
所以keys_view
和values_view
似乎完全没有用。
在标准中引入它们的目的是什么?我是否错过了一些有用的用例?
示例中缺少的 pair
问题只是示例中的一个错误;我提交了 editorial pull request.
更大的问题在于 keys_view
和 values_view
的定义。已提交 LWG 问题,我已为其提供了建议的解决方案。这里的基本问题是
template <class R>
using keys_view = elements_view<views::all_t<R>, 0>;
仅在非推导上下文中使用 R
,因此它不可能满足别名模板 CTAD 中 the arguments of the alias template be deducible from whatever type that is eventually deduced.
的要求
我提议的决议只是将 keys_view
重新定义为
template <class R>
using keys_view = elements_view<R, 0>;
这允许 CTAD 在您已经有一个视图的情况下工作,并且至少让它有机会使用稍后添加的 elements_view
上的任何演绎指南。我探讨了添加此类指南的可能性,但当前的别名模板 CTAD 实现太破旧,无法对其进行测试,因此我不愿意提出一个建议。
有了这个改变,你至少可以写
auto names = keys_view{views::all(historical_figures)};
for (auto&& name : names) {
cout << name << ' '; // prints Babbage Hamilton Lovelace Turing
}
C++20 引入了 ranges::elements_view
,它接受 view
的 类似元组的 值,并发出一个 view
值类型 改编后的 view
值类型的第 N 个元素,其中 N 是非类型模板参数。
在[range.elements.view]中ranges::elements_view
的概要定义为:
template<input_range V, size_t N>
requires view<V> && has-tuple-element<range_value_t<V>, N> &&
has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
returnable-element<range_reference_t<V>, N>
class elements_view : public view_interface<elements_view<V, N>> {
public:
elements_view() = default;
constexpr explicit elements_view(V base);
// ...
};
由于elements_view
的构造函数只包含参数V
,所以不指定所有模板参数(godbolt)是无法直接初始化elements_view
的:
std::array<std::tuple<int, float>, 5> r{};
ranges::elements_view<0>{r}; // error: wrong number of template arguments (1, should be 2)
ranges::elements_view<decltype(views::all(r)), 0>{r}; // ok
views::elements<0>(r); // ok
这使得它失去了其他范围适配器的表达式等价性,例如 ranges::transform_view
:
ranges::transform_view{r, [](auto elem) { return std::get<0>(elem); }}; // ok
views::transform(r, [](auto elem) { return std::get<0>(elem); }); // ok
并且在elements_view
的基础上,标准新增了keys_view
和values_view
,它们都是elements_view<views::all_t<R>, N>
的别名:
template <class R>
using keys_view = elements_view<views::all_t<R>, 0>;
template <class R>
using values_view = elements_view<views::all_t<R>, 1>;
该标准在 [range.elements#overview-example-2]:
中给出了它的用例auto historical_figures = map{
{"Lovelace"sv, 1815},
{"Turing"sv, 1912},
{"Babbage"sv, 1791},
{"Hamilton"sv, 1936}
};
auto names = keys_view{historical_figures};
for (auto&& name : names) {
cout << name << ' '; // prints Babbage Hamilton Lovelace Turing
}
也就是not quite right,因为我们无法推导出R
的类型,所以构造keys_view
的唯一方法就是指定所有模板参数,就像elements_view
一样:
auto names = std::ranges::keys_view<decltype(std::views::all(historical_figures))>{historical_figures};
所以keys_view
和values_view
似乎完全没有用。
在标准中引入它们的目的是什么?我是否错过了一些有用的用例?
示例中缺少的 pair
问题只是示例中的一个错误;我提交了 editorial pull request.
更大的问题在于 keys_view
和 values_view
的定义。已提交 LWG 问题,我已为其提供了建议的解决方案。这里的基本问题是
template <class R>
using keys_view = elements_view<views::all_t<R>, 0>;
仅在非推导上下文中使用 R
,因此它不可能满足别名模板 CTAD 中 the arguments of the alias template be deducible from whatever type that is eventually deduced.
我提议的决议只是将 keys_view
重新定义为
template <class R>
using keys_view = elements_view<R, 0>;
这允许 CTAD 在您已经有一个视图的情况下工作,并且至少让它有机会使用稍后添加的 elements_view
上的任何演绎指南。我探讨了添加此类指南的可能性,但当前的别名模板 CTAD 实现太破旧,无法对其进行测试,因此我不愿意提出一个建议。
有了这个改变,你至少可以写
auto names = keys_view{views::all(historical_figures)};
for (auto&& name : names) {
cout << name << ' '; // prints Babbage Hamilton Lovelace Turing
}