Representing a dummy argument via STL or Boost

Suppose we want to fill a vector with random numbers. Then there is the following obvious solution:

vector<int> result; result.resize(n); for (int i = 0; i < n; ++i) { result[i] = generateRandomNumber(); } 

OK, this obviously works, but I would like to understand what is the simplest STL / Boost way to get rid of the for loop. It is tempting to use std :: transform, but it takes a function with one argument. Is there any good STL way to represent a dummy argument in a function?

+4
source share
3 answers

The C ++ standard library has std::generate() and std::generate_n() ;

For instance:

 #include <iostream> #include <cstdlib> #include <algorithm> #include <vector> #include <iterator> int generateRandomNumber() { return std::rand(); } int main() { int n = 10; std::vector<int> result; generate_n(back_inserter(result), n, generateRandomNumber); copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; } 

test: https://ideone.com/5xD6P

As for the second question, if I understand it correctly, then how to create a functor that takes an int argument, ignores it and calls your int f() ?

C ++ 98 way - actually write the whole functor:

 struct IgnoreArgument { typedef int(*fp_t)(); fp_t fp; IgnoreArgument(fp_t f) : fp(f) {} int operator()(int) const { return fp(); } }; ... transform(v.begin(), v.end(), v.begin(), IgnoreArgument(f)); 

test: https://ideone.com/DTsyl

C ++ 11 - using lambda expressions

 transform(v.begin(), v.end(), v.begin(), [](int){return f();}); 

test: https://ideone.com/nAPXI

And the way C ++ 98 / boost is to use boost::bind

 transform(v.begin(), v.end(), v.begin(), boost::bind(f)); 

test: https://ideone.com/cvd88

+5
source

Use generate_n with the number of elements you want, along with the back_insert_iterator to the vector where you want to store them, and a pointer to a function that generates numbers.

 #include <vector> #include <algorithm> int generateRandomNumber() { static int i = 0; return 42 + (i++); } int main() { std::vector<int> vi; std::generate_n(back_inserter(vi), 10, &generateRandomNumber); } 

Note that with back_insert_iterator , as I am here, you do not need to pre-format the vector, which is kludgy at best.

+1
source

The problem is that transform simply not the right choice for the task. The goal of transform is to take some inputs, transform them in a specific way, and create output for each of these inputs.

In this case, you have no input. transform makes sense if the values ​​in the vector were (one way or another) based on the values ​​in any existing vector.

generate_n is really the right solution to the problem - it is designed to call some function / functor N times, producing N results and assigning them to the output iterator (and its successors) that you supply. Since it is designed to generate values ​​(instead of converting existing values), the function / functor does not accept input, and you do not need to provide a "fake" input.

As for the dummy argument, the need for it is probably a pretty good sign that (as in this case) you are using the wrong algorithm, and simply shouldn't do it.

However, you sometimes find yourself in the opposite situation: you want to use an algorithm that does not provide an argument, but you want to have an argument. For example, suppose you wanted to be able to set the numbers in your array to a random number with some lower and / or upper bound specified by the user. In this case, you want to specify the boundaries that will be passed to your random function, but there are no conditions for either generate or generate_n .

In this case, you have two options. One of them is bind (originally boost::bind , but now included in C ++ 11). As a rule, I prefer to use a functor, and pass the argument to ctor:

 class gen_random { int lower; int upper; public: gen_random(int lower = 0, int upper = RAND_MAX) : lower(lower), upper(upper) {} int operator() { return rand_range(lower, upper); }; int main() { std::vector<int> rand_ints; std::generate_n(std::back_inserter(rand_ints), 10, gen_random(1, 6)); return 0; } 
+1
source

All Articles