d3r0n
d3r0n

Reputation: 128

Generate matrix with std::generate

Using reference. The behavior of std::generate should be equivalent to:

template <class ForwardIterator, class Generator>
  void generate ( ForwardIterator first, ForwardIterator last, Generator gen )
{
  while (first != last) {
    *first = gen();
    ++first;
  }
}

But when I try to fill matrix with 0-99 numbers. Instead I get 10 rows of 0-9. Why is that?

std::vector< std::vector<int> > m;

#ifdef GENERATE
    struct Generator{
        int seq;
        Generator():seq(0){};
        int operator()(){return seq++;};
    } generator;
#elif defined FOR_EACH
    int i = 0;
    auto increment = [&i](int &a){a = i++;};
#endif

int x = 10, y = 10;
m.resize(x);
for(auto &v : m)
{
    v.resize(y);
    #ifdef GENERATE
        generate(begin(v), end(v), generator);
    #elif defined FOR_EACH
        for_each(begin(v),end(v), increment);
    #endif
}

Upvotes: 0

Views: 247

Answers (3)

Sebastian Mach
Sebastian Mach

Reputation: 39109

Because your generator is copied according to the signature

void generate ( ForwardIterator first, ForwardIterator last, Generator gen )
                                                  copied ----^^^^^^^^^^^^^

i.e. you keep your own copy, std::generate does as well. Every time you pass your own, thus unmodified generator into std::generate, i.e. each time you begin with a version that has its counter 0.

You need to somehow preserve the counter in your functor and share it across uses of Generator, e.g.

struct Generator{
    int *seq;
    Generator(int &seq):seq(&seq){};
    int operator()(){return (*seq)++;};
};

....

int seq = 0;
generate(begin(v), end(v), Generator(seq));

or equivalently

int seq = 0;
generate(begin(v), end(v), [&seq]() { return seq++; });

Upvotes: 2

Konstantin Tenzin
Konstantin Tenzin

Reputation: 12948

You may also use std::ref to prevent copying of Generator object:

generate(begin(v), end(v), std::ref(generator));

Upvotes: 3

legends2k
legends2k

Reputation: 33004

If case of std::for_each, you pass the lambda function increment which takes the automatic variable int i = 0; by reference. For every invocation for_each it creates a new increment functor, but i is not reset but just incremented; hence you see the values being continuous. The memory of the lambda function (i) is outside its own domain i.e. in the parent function main.

In case of your own generate function, the resource is within the structure i.e. Generator::seq is reinitialized. For every invocation of generate, a new Generator is created; its constructor initializes seq to 0, there by resetting the count every time.

#ifdef GENERATE
int i = 0;       // lives in main
struct Generator {
    int &seq;
    Generator(int &i):seq(i) { }
    int operator()() { return seq++; }
} generator(i);
#else

This should work, since the memory part of the Generator is now at a higher level.

Upvotes: 2

Related Questions