首页 > 解决方案 > C++20 keys_view 和 values_view 的真正目的是什么?

问题描述

C++20 引入了ranges::elements_view,它接受 a viewof tuple-like值,并发出 aview值类型为adapted 的值类型的第N个元素view,其中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只包含参数,所以不指定所有模板参数(godboltV )就无法直接初始化: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_viewvalues_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  
} 

不太对,因为我们无法推断 的类型R,唯一的构造方法keys_view是指定所有模板参数,就像elements_view

auto names = std::ranges::keys_view<decltype(std::views::all(historical_figures))>{historical_figures};

因此keys_viewvalues_view似乎完全没用。

在标准中引入它们的目的是什么?我是否错过了一些有用的用例?

标签: c++c++20std-ranges

解决方案


示例中缺少的pair问题只是示例中的一个错误;我提交了一个编辑拉取请求

更大的问题在于keys_viewandvalues_view的定义。已提交 LWG 问题,我已为此提供了建议的解决方案。这里的基本问题是

template <class R>
using keys_view = elements_view<views::all_t<R>, 0>;

仅在非推导上下文中使用R,因此它不可能满足别名模板 CTAD 中的要求,即别名模板的参数可以从最终推导的任何类型中推导出来

我提出的决议只是重新定义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  
} 

推荐阅读