Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functor's instance variable is not kept between consecutive calls to generate_n

Tags:

c++

functor

stl

I am using the following struct as an input to STL's generate_n algorithm:

struct GenerateNumber {     
    GenerateNumber () : i(0) {}
    int operator () (void) {        
        return  i++;
    }
private:
    int i;
};

An example to the code that uses this functor is:

std::vector <int> v1 (3);
std::vector <int> v2 (3);
GenerateNumber generateNumber;
std::generate_n (v1.begin (), 3, generateNumber);
std::generate_n (v2.begin (), 3, generateNumber);

However, the result is that both v1 and v2 contain {0,1,2}, instead of v2 to contain {3,4,5}. I have verified with a breakpoint that the GenerateNumber's constructor is called only once (I know it doesn't make sense that the constructor is called more then once, but I checked it anyway).

I know I can solve this by making i static, but I don't understand this behavior. How come the value of i is not kept between consecutive calls?

like image 429
Itamar Katz Avatar asked May 13 '12 09:05

Itamar Katz


2 Answers

The generator object is copied when passed to generate_n. Try using std::ref, i.e.

std::generate_n(v1.begin(), 3, std::ref(generateNumber));
std::generate_n(v2.begin(), 3, std::ref(generateNumber));

Edit: Note that std::ref is only available in C++11. It was introduced in TR1 as std::tr1::ref, and is also available in boost as boost::ref.

like image 158
nosid Avatar answered Sep 30 '22 03:09

nosid


std::generate_n takes the functor by value, that is, it makes a copy of it. It could be that you didn't check that the copy constructor was being called.

In the absence of std::ref, and if your problem is localised as in your example, you could modify your functor to take a reference to a counter set in the scope of the calls to std::generate_n:

struct GenerateNumber {     
    GenerateNumber (int& i) : struct GenerateNumber {     
    GenerateNumber () : i(0) {}
    int operator () (void) {        
        return  i++;
    }
private:
    int& i;
};

int main() {
  int counter = 0;
  std::vector <int> v1 (3);
  std::vector <int> v2 (3);
  GenerateNumber generateNumber(counter);
  std::generate_n (v1.begin (), 3, generateNumber);
  std::generate_n (v2.begin (), 3, generateNumber);

}
like image 20
juanchopanza Avatar answered Sep 30 '22 02:09

juanchopanza