einpoklum
einpoklum

Reputation: 132138

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

Consider the following program:

#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;
}

This compiles and prints 10; but - if I uncomment the (*) line - it doesn't compile, with GCC complaining about the left side of the equality not being an lvalue. I was kind of disappointed by that - I was sort of hoping the transformation would produce int&'s which I could assign to...

Is there something I can to make this a modifiable view? Or some other mechanism in the ranges library which would allow me the equivalent of a modifiable view?

Upvotes: 4

Views: 689

Answers (1)

Deduplicator
Deduplicator

Reputation: 45684

The problem with your code is very simple, if you think about it:

The transformation-function is actually a projection-function, and yours doesn't produce the references necessary for allowing modification of the source, because the standard return type deduction rule for lambdas uses the rules for plain auto, and those never deduce references.

  1. One fix is changing to the deduction rules for decltype(auto), which preserve references, and are thus better avoided unless you know they are right.

    auto int_view = tuples | ranges::view::transform(
        [](auto& t)->decltype(auto){return std::get<int>(t);});
    
  2. Or you can explicitly ask for references with auto& or something more specific.

    auto int_view = tuples | ranges::view::transform(
        [](auto& t)->auto&{return std::get<int>(t);});
    
  3. And finally nobody stops you from returning a proxy like std::reference_wrapper. Though that's a needless complication.

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

Upvotes: 3

Related Questions