是否有范围::视图::转换的可修改视图版本

Is there a modifiable-view version of ranges::view::transform?

本文关键字:视图 修改 版本 转换 是否 范围      更新时间:2023-10-16

请考虑以下程序:

#include <iostream>
#include <algorithm>
#include <numeric>
#include <array>
#include <range/v3/view/transform.hpp>
int main() {
    using container = std::array<std::tuple<int,float,double>, 4>;
    container tuples {{
        {1, 4.f, 8.},
        {2, 5.f, 9.},
        {3, 6.f, 10.},
        {4, 7.f, 11.}
    }};
    auto int_view =
        tuples | ranges::view::transform( [](auto& t){return std::get<int>(t);} );
    // int_view[1] = 3; // (*)
    auto x = std::accumulate(int_view.begin(), int_view.end(), 0);
    std::cout << "x = " << x << std::endl;
}

这编译并打印10 ; 但是 - 如果我取消注释(*)行 - 它不会编译,GCC 抱怨等式的左侧不是左值。我对此有点失望 - 我有点希望这种转变会产生int&我可以分配给......

我能做些什么来使它成为一个可修改的视图吗?或者范围库中的其他一些机制可以让我等效于可修改的视图?

如果你考虑一下,你的代码的问题非常简单:

转换函数实际上是一个投影函数,你的函数不会产生允许修改源所需的引用,因为 lambda 的标准返回类型推导规则使用普通auto的规则,而这些规则永远不会推导出引用。

  1. 一种解决方法是更改为decltype(auto)的演绎规则,该规则保留了引用,因此最好避免使用,除非您知道它们是正确的。

    auto int_view = tuples | ranges::view::transform(
        [](auto& t)->decltype(auto){return std::get<int>(t);});
    
  2. 或者,您可以明确要求引用auto&或更具体的内容。

    auto int_view = tuples | ranges::view::transform(
        [](auto& t)->auto&{return std::get<int>(t);});
    
  3. 最后没有人阻止您返回像std::reference_wrapper这样的代理.虽然这是一个不必要的复杂化。

    auto int_view = tuples | ranges::view::transform(
        [](auto& t){return std::ref(std::get<int>(t));});