Home > Back-end >  C 20 using ranges pipe | modify the value in vector
C 20 using ranges pipe | modify the value in vector

Time:02-19

This is my code :

std::vector<int> vec = {1, 2, 3, 4, 5};

auto even = [=](int i) {
    if (i % 2 == 1) {
        return true;
    }
    return false;
};

auto square = [=](int i) {
    return i * i ;
};

for (auto &item : vec | std::views::filter(even) | std::views::transform(square)) {

    std::cout << item << std::endl;
}

I want to get the vec result through | (pipe) such like this : 1 2 9 4 25

but compiler tells me

E:\Source Files\Jetbrains\Clion\moderncpp\main.cpp: In function 'int main()':
 E:\Source Files\Jetbrains\Clion\moderncpp\main.cpp:21:84: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
   21 |     for (auto &item : vec | std::views::filter(even) | std::views::transform(square)) {
      |                                                                                    ^
mingw32-make[3]: *** [CMakeFiles\moderncpp.dir\build.make:81: CMakeFiles/moderncpp.dir/main.cpp.obj] Error 1

what could i do if i want to change my value in vector using pipe |

Thanks !

CodePudding user response:

Here you try taking a non-const reference to an rvalue which isn't allowed:

for(auto &item : vec | ...

Either take it by const&:

for(const auto &item : vec | ...

or by value:

for(auto item : vec | ...

what could i do if i want to change my value in vector

If you really want to change the values in the vector<int> you could use the transform_view to create a new vector<int> that you move assign to vec:

#include <iostream>
#include <ranges>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    auto even = [](int i) { return i % 2 == 0; }; // bugfixed and no capture

    auto square = [](int i) { return i * i; };    // no capture

    // create transform_view
    auto trans = vec | std::views::filter(even) | std::views::transform(square);

    // populate a temporary vector that you assign to `vec`
    vec = std::vector<int>(trans.begin(), trans.end());

    for (auto& item : vec) {
        std::cout << item << '\n';
    }
}

With Range-v3 you could create the vector<int> directly using ranges::to<std::vector<int>>(). Unfortunately, this doesn't exist in the C 20 standard library (but will hopefully be added in the future):

#include <range/v3/all.hpp>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto even = [](int i) { return i % 2 == 0; };
    auto square = [](int i) { return i * i; };

    vec = vec | ranges::views::filter(even)
              | ranges::views::transform(square)
              | ranges::to<std::vector>();       // using ranges::to

    for (auto& item : vec) {
        std::cout << item << '\n';
    }
}

CodePudding user response:

I want to get the vec result through | (pipe) such like this : 1 2 9 4 25

It looks like you want to square the odd-valued elements in vec, try this:

std::vector vec = {1, 2, 3, 4, 5};
std::ranges::for_each(
  vec | std::views::filter([](auto& elem) { return elem % 2 == 1; }), 
  [](auto& elem)  { elem *= elem; });

Demo

CodePudding user response:

what could i do if i want to change my value in vector using pipe |

You can transform the vector in-place using an algorithm like std::ranges::transform, but you need to cut out the extra elements:

#include <iostream>
#include <ranges>
#include <vector>
#include <algorithm>
#include <concepts>

int main()
{
  std::vector<int> vec = {1, 2, 3, 4, 5};

  constexpr auto even = [](std::integral auto i) noexcept {
    return i % 2 == 0;
  };

  constexpr auto square = [](auto i) noexcept {
    return i * i;
  };

  // Create view.
  constexpr auto my_view = std::views::filter(even)
                         | std::views::transform(square);

  // Show the resulting view (4 16), without changing the source vector.
  for (auto item : vec | my_view)
  { //             ^^^^^^^^^^^^^
      std::cout << item << ' ';
  }
  std::cout << '\n';

  // Use an algorithm to transform the vector...
  auto [ignored, it] = std::ranges::transform( vec | my_view
                                             , vec.begin()
                                             , std::identity{} );
  // But erase the filtered out elements.
  vec.erase(it, vec.end());

  for (auto item : vec)          // Outputs  4 16
  { //             ^^^
      std::cout << item << ' ';
  }
  std::cout << '\n';
}
  •  Tags:  
  • c
  • Related