My question/concern is about the function parameter to be used in std::transform().
In the following codes, if I used a "pass-by-reference" in the integer parameter i of the squared function (i.e. int squared(int i)), it does not compile.
I have to change it to pass by value so that it compiles. Can anyone please tell me why and if this is a cosntraint for using std::transform()?
The std::for_each() is fine with using both "pass-by-value" and "pass-by-reference" approaches (as shown in print()).
Thanks in advance.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int squared(int i);
void print(int &i);
int main()
{
std::vector<int> intVec;
std::vector<int> newIntVec;
for (int i = 0; i < 10; ++i)
intVec.push_back(i);
std::for_each(intVec.begin(), intVec.end(), print);
std::cout << std::endl;
std::transform(intVec.begin(), intVec.end(), std::back_inserter(newIntVec), squared);
std::for_each(newIntVec.begin(), newIntVec.end(), print);
std::cout << std::endl;
return 0;
}
int squared(int i)
{
return i*i;
}
void print(int &i)
{
std::cout << i << " ";
}
For a std::transform, the operator should have no side-effects (it should take the input and provide an output). So you should try making the reference const:
int squared(const int &i)
{
return i*i;
}
To quote from CPP Reference, "[the function] must not have side effects" (C++), and also "[the function] must not invalidate any iterators, including the end iterators, or modify any elements of the ranges involved." (C++11)
This basically means that what gets passed to your function should be considered immutable... hence if you pass by reference, it should be a const reference.
Conversely, std::for_each operates on the series of data you pass it, meaning that the values can be modified.
Related
I know we can iterate through an array passed as an argument in this way:
// NO ERROR
void fun(int *a, int n){
for(int i=0; i<n; i++)
cout<<a[i];
}
But, is there any way I could iterate through an array using a for-each loop inside a function like this?
// ERROR
void fun(int *a, int n){
for(auto x:a)
cout<<x;
}
A pointer is not an array. If you pass a pointer to the first element of an array to a function then its no longer an array, but a pointer.
You can use a range based loop when you pass the array by reference:
#include <iostream>
template <size_t N>
void foo(int (&x)[N]) {
for (int i : x) std::cout << i << " ";
}
int main() {
int x[] = {1,2,3};
foo(x);
}
Output:
1 2 3
This works, because the range based loop uses std::begin(x) and std::end(x) to get iterators to the begin and end of the array. Pointers don't have a begin or end.
In C++20, you can use std::span:
#include <cstddef>
#include <cstdio>
#include <span>
void foo(int* arr, std::size_t sz) {
std::span<int> span{arr, sz};
for (int elm : span) {
std::printf("%d\n", elm);
}
}
Or you could make span the input argument in the first place:
void foo(std::span<int> span) {
for (int elm : span) {
std::printf("%d\n", elm);
}
}
If the signature of the function is flexible, I suggest you use the second option.
Pre C++20, here is an implementation of span from GSL. Or make your own wrapper class with begin() and end() functions.
Alternative non-template C++20 solution:
auto range = std::views::counted(arr, sz);
for (auto elm : range) {
The benefit of this compared to std::span is that this is more general and works with any iterator, not requiring a contiguous iterator such as a pointer.
The benefit of using std::span instead of this is that you can use std::span as the function parameter without making it a template.
Alternative template solution (works pre C++20):
template <class Range>
void foo(const Range& range) {
for (auto elm : range) {
The benefit of this compared to int (&arr)[N] is that it is much more general. This template works with all ranges.
Besides range-for, you could consider avoiding the loop entirely (works pre C++20):
auto print = [](auto elm) {
std::cout << elm;
}
std::for_each_n(arr, sz, print);
I recommend this if you don't have C++20, cannot have boost / ranges / GSL libraries for some reason, and cannot have a template.
yes, you can. Just pass the array by reference to a template function:
#include <iostream>
using namespace std;
template <size_t N> void foo(int (&arr)[N])
{
for (auto i:arr)
cout << i << " ";
}
int main()
{
int array[] = { 5, 17, 3452, 546546, 756756, 75675, 756753, 345, 53};
foo(array);
return 0;
}
And the real answer:
int made[] = {10 , 2 ,15};
std::for_each(std::begin(made),std::end(made),[=](auto x){ std::cout << x << std::endl; });
You can parallelize std::for_each with say std::execution::par.
With function code will be:
#include <iostream>
#include <vector>
#include <execution>
void f(int (&made)[3])
{
std::for_each(std::begin(made),std::end(made),[=](auto x){ std::cout << "and then " << x << std::endl; });
}
int main(int argc , char *argv[])
{
int made[] = {10 , 2 ,15};
f(made);
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template <class T>
class Sum {
public:
Sum(T i = 0) : res(i) {}
void operator()(T x) { res =res + x; }
T result() const { return res; }
private:
T res;
};
int main() {
Sum<int> s;
vector<int> vec;
vec.insert(vec.begin(), 10);
vec.insert(vec.begin()+1, 10);
vec.insert(vec.begin()+2, 10);
vector<int>::iterator itr = vec.begin();
cout << *itr << endl;
for_each(vec.begin(), vec.end(), s);
cout << "sum is" << s.result() << endl;
return 0;
}
This is my code. I want to add vec values in class Sum res. for_each should be calling s's operator(), so the result should be 30, but it shows 0.
I think adding value in vector has no problem. Why is the s.operator() is not working?
for_each takes its third argument by value which means every invocation of operator() affects a completely separate copy of s. There's an algorithm for exactly what you're doing called std::accumulate, but if you want this to work with for_each you need to pass s "by reference" by using std::ref from <functional>.
for_each(vec.begin(), vec.end(), ref(s));
for_each returns a copy of the passed-in functor that provides the "result" of the iteration (whatever the result is). Change your call to:
auto s = for_each(vec.begin(), vec.end(), Sum<int>());
I am learning functors and their usages. I came across below code in one of stack overflow questions.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class CalculateAverage
{
private:
std::size_t num;
double sum;
public:
CalculateAverage() : num (0) , sum (0)
{
}
void operator()(int elem)
{
num++;
sum += elem;
}
operator double () const
{
return sum / num;
}
};
int main()
{
vector<int> values;
for (int i = 1; i <= 5; i++)
values.push_back(i);
int average = std::for_each(values.begin(), values.end(), CalculateAverage());
cout << average << endl;
}
Output : 3
I understood functor concept. But I didn't understand why conversion function is getting called. std::for_each() returns the same function that we pass to for_each() as the last argument. Here we are passing () overloaded function. Can some one please help me to understand why conversion function is getting called in this case?
You are not passing a function to std::for_each, you are passing a functor (an object that acts like a function). When std::for_each returns it returns the function object you passed it. Your function object is of type CalculateAverage but you are trying to assign it to an int. So the conversion operator is chosen to convert the returned CalculateAverage into a double and then that is used to initialize average.
In a program I'm working on, I've declared a vector in main. I have two functions that use the vector: an int function, and a standard void 'print' function. I'm attempting to use a function pointer (pointing to the int function) in the void function, but I get the error that the vector has not been declared, even though it's in main. I've tried declaring the vector outside of main, and the function worked fine, but I'm hesitant on keeping it outside of main. I was wondering if there was some way to use the vector in the void function when it was declared in main. Here's some example code for what I'm asking:
// Example program
#include <iostream>
#include <vector>
using namespace std;
int returnSquare(vector<int>& numbers);
void print(int (*squarePtr)(vector<int>&));
int (*squarePtr)(vector<int>&);
int main()
{
vector<int> v(1);
squarePtr = &returnSquare;
for(int i = 0; i < v.size(); i++)
{
v.at(i) = i * 25;
cout << v.at(i) << " ";
}
print(squarePtr);
return 0;
}
int returnSquare(vector<int>& numbers)
{
int product = 0;
for(int i = 0; i < numbers.size(); i++)
{
product = numbers.at(i) * numbers.at(i);
}
return product;
}
void print(int (*squarePtr)(vector<int>&))
{
int answer = (*squarePtr)(v);
cout << answer << endl;
}
In your function print you have just one parameter. To call your squaring function you need to pass the vector to square to it, something like:
void print(int (*squarePtr)(vector<int>&), vector<int> &v)
{
int answer = (*squarePtr)(v);
cout << answer << endl;
}
Without that the variable v is not visible inside the function. The call should look like:
print(squarePtr, v);
Less important. You use the squarePtr name in your global definitions twice. This does not bring clarity to your code. You better write:
void print(int (*workerFuncPtr)(vector<int>&));
int (*squarePtr)(vector<int>&);
void print (int (*squarePtr)(vector<int>&))
This parameter only accepts a pointer to your square function, that's it. It doesn't hold a pointer or a reference to your v variable. Your print function has no idea what v is, unless you pass v as an argument, or make v a global variable (which is what you did moving it out of main).
Do this:
// Modify your print definition to accept a reference to your vector `v`
void print(int (*squarePtr)(vector<int>&), vector<int>& v);
// Add `v` as the second argument to your print call
print(squarePtr, v);
// Modify the definition of your print function to accept a reference to your vector `v`
void print(int (*squarePtr)(vector<int>&), vector<int>& v)
{
...
}
http://www.cplusplus.com/reference/algorithm/for_each/
Unary function taking an element in
the range as argument. This can either
be a pointer to a function or an
object whose class overloads
operator(). Its return value, if any,
is ignored.
According to this article, I expected that for_each actually modifies the object given as its third argument, but it seems like for_each operates on a temporary object, and doesn't even modify the object given to it.
So, why is it implemented in that way? It seems much less useful. Or did I misunderstand something and my code below contains errors?
#include <iostream>
#include <vector>
#include <algorithm>
template <class T> struct Multiplicator{
T mresult;
public:
const T& result() const{return mresult;}
Multiplicator(T init_result = 1){
mresult = init_result;
}
void operator()(T element){
mresult *= element;
std::cout << element << " "; // debug print
}
};
int main()
{
std::vector<double> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
Multiplicator<double> multiply;
std::for_each(vec.begin(),vec.end(),multiply);
std::cout << "\nResult: " << multiply.result() << std::endl;
return 0;
}
Expected output:
1 2 3 Result: 6
But got following output:
1 2 3 Result: 1
The function object is taken by value. for_each returns the function object, so if you change it to:
multiply = std::for_each(vec.begin(),vec.end(),multiply);
you get the expected output.
While James is correct, using std::accumulate with std::multiplies would be more correct, probably:
#include <iostream>
#include <functional>
#include <numeric>
#include <vector>
int main(void)
{
std::vector<double> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
double result = std::accumulate(vec.begin(), vec.end(),
1.0, std::multiplies<double>());
std::cout << "\nResult: " << result << std::endl;
}
With your for_each version, you don't really need to copy the functor again, rather:
double result = std::for_each(vec.begin(), vec.end(), multiply).result();
Or C++0x, for fun:
double result = 1;
std::for_each(vec.begin(), vec.end(), [&](double pX){ result *= pX; });
The semantics of For_each dont fit into what you are trying to do. accumulate does exactly what you are trying, use that instead.