Home > Software design >  use std::accumulate to add an array to only a vector slice
use std::accumulate to add an array to only a vector slice

Time:09-07

I have following code

  std::vector<float> d; 
  d.resize(800); 


  std::array<float, 8> adder;

   int ind_slice = 5; // we want to add the array adder to v[40],v[41] ... v[47] 

   const auto it_begin = d.begin()   ind_slice *8;
   const auto it_end = d.begin()   ind_slice *8   ind_slice;
   int index = 0;
   std::accumulate(it_begin, it_end) [&] ( float* ind) { return ind = ind   (adder[index  ])};

I am wondering if this is a safe way to do the accumulation, since I am captuing a reference from the outside and mutating it. So the function does have side effects. Is there a better way to use the accumulate to achieve my objective

CodePudding user response:

At least if I understand your intent correctly, the algorithm to use here would almost certainly be std::transform, not std::accumulate.

accumulate is intended for taking some collection, and simply adding them up, roughly equivalent to sum() in a spreadsheet (for one example).

transform allows you (among other things) to combine two collections, about the way you seem to want to.

#include <vector>
#include <array>
#include <algorithm>
#include <iterator>
#include <iostream>

int main() {
    std::vector<float> d; 
    d.resize(800); 

    std::array<float, 8> adder { 1, 2, 3, 4, 5, 6, 7, 8};

    size_t start = 40;
    const auto it_begin = d.begin()   start;
    const auto it_end = d.begin()   start   adder.size(); 

    // do the addition:
    std:transform(it_begin, it_end, adder.begin(), it_begin, [](float a, float b) { return a   b; });

    // show the modified part of the array.
    std::copy(it_begin, it_end, std::ostream_iterator<float>(std::cout, "\n"));
}

I've taken the liberty of simplifying a bit of the other code as well, but not in ways that are likely to matter much here.

Since you only need an iterator to the beginning of the second collection, you can simplify the code a bit further if you want, by using adder as the first collection, and the slice of d as the second:

#include <vector>
#include <array>
#include <algorithm>
#include <iterator>
#include <iostream>

int main() {
    std::vector<float> d; 
    d.resize(800); 

    std::array<float, 8> adder { 1, 2, 3, 4, 5, 6, 7, 8};

    size_t start = 40;
    const auto it_begin = d.begin()   start;

    std:transform(adder.begin(), adder.end(), it_begin, it_begin, [](float a, float b) { return a   b; });

    std::copy(it_begin, it_begin adder.size(), std::ostream_iterator<float>(std::cout, "\n"));
}

As it stands right now, we still use an iterator to the end of the affected portion of d when we print things out, but that was added just to make it clear that we'd actually done something, not to fulfill any real requirement.

CodePudding user response:

Since C 11 the binary operator passed to std::accumulate is allowed to have side effects. The restrictions since C 11 are (from cppreference):

op must not invalidate any iterators, including the end iterators, nor modify any elements of the range involved, nor *last.

In general, standard algorithms are permitted to copy functors passed to them, but this isnt an issue with your operator either.

The problem is that the functor passed to std::accumulate must be a binary operator that specifies how each element is accumulated to the resulting value. Yours is not a binary operator and it will simply not compile. It is not clear why you want to use std::accumulate when you do not want to accumulate something. A simple loop will do:

for (size_t i=0;i<8;  i) d[i 40]  = adder[i];
  • Related