Passing the Functor-parameters in for_each() and transform()

A

Alex Vinokur

Functor-parameters in the for_each() and transform() algorithms are passed by value.

Might it make sense to have also algorithms for_each2() and transform2()
which pass Functor-parameters by non-const reference?


Here is some program which demonstrates probable necessity
in such forms of for_each() and transform().


====== C++ code : File foo.cpp : BEGIN ======

#include <vector>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>
using namespace std;

#define MAX_VALUE(x,y) ((x) > (y) ? (x) : (y))

class Foo
{
friend ostream& operator<< (ostream& os, const Foo& ioo);

private :
int sum_;
vector<int> vect_;

public :
Foo (vector<int> x, vector<int> y) : sum_ (0)
{
const unsigned max_size = MAX_VALUE (x.size (), y.size ());

x.resize(max_size);
y.resize(max_size);
vect_.resize(max_size);

// ------ transform ------
transform (x.begin(), x.end(), y.begin(), vect_.begin(), *this);
cout << "sum_ = " << sum_ << " (after transform)" << endl;
cout << (*this) << endl;
// It seems that it is impossible to change sum_ through transform because
// 1) Foo copy constructor is called;
// 2) transform() doesn't return Functor-object
// -----------------------

// ------ for-each -------
for_each (vect_.begin(), vect_.end(), *this);
cout << "sum_ = " << sum_ << " (after for_each-1)" << endl;
cout << (*this) << endl;
// sum_ is not changed because
// 1) Foo copy constructor is called;
// 2) Foo value returned is not used
// -----------------------

// ------ for-each -------
*this = for_each (vect_.begin(), vect_.end(), *this);
cout << "sum_ = " << sum_ << " (after for_each-2)" << endl;
cout << (*this) << endl;
// sum_ is changed because
// * Foo value returned is used to change *this
// -----------------------

// ------ for-each -------
*this = for_each (vect_.begin(), vect_.end(), *this);
cout << "sum_ = " << sum_ << " (after for_each-3)" << endl;
cout << (*this) << endl;
// sum_ is changed because
// * Foo value returned is used to change *this
// -----------------------

}

int operator() (int n1, int n2)
{
sum_ += n1;
sum_ += n2;
return (n1 + n2);
}

void operator() (int n)
{
sum_ += n;
}


};


ostream& operator<< (ostream& os, const Foo& ioo)
{
ostringstream oss;
oss << "{ vect_ = ";
copy (ioo.vect_.begin(), ioo.vect_.end(), ostream_iterator<int> (oss, " "));
return os << oss.str() << "}" << endl;
}


int main ()
{
const int a1[] = {1, 2, 3, 4, 5};
const int a2[] = {10, 20, 30, 40, 50, 60, 70};
const vector<int> v1 (a1, a1 + sizeof(a1)/sizeof(*a1));
const vector<int> v2 (a2, a2 + sizeof(a2)/sizeof(*a2));

{ Foo (v1, v2);}

return 0;
}

====== C++ code : File foo.cpp : END ========



====== Compilation & Run : BEGIN ======

$ g++ -v
[---omitted---]
gcc version 3.3.1 (cygming special)

$ g++ foo.cpp

$ a
sum_ = 0 (after transform) // sum_ not changed
{ vect_ = 11 22 33 44 55 60 70 }

sum_ = 0 (after for_each-1) // sum_ not changed
{ vect_ = 11 22 33 44 55 60 70 }

sum_ = 295 (after for_each-2) // sum_ changed
{ vect_ = 11 22 33 44 55 60 70 }

sum_ = 590 (after for_each-3) // sum_ changed
{ vect_ = 11 22 33 44 55 60 70 }

====== Compilation & Run : END ========

--
=====================================
Alex Vinokur
mailto:[email protected]
http://mathforum.org/library/view/10978.html
news://news.gmane.org/gmane.comp.lang.c++.perfometer
=====================================
 
A

Agent Mulder

Might it make sense to have also algorithms for_each2() and transform2()
which pass Functor-parameters by non-const reference?
</>

Hi Alex, I seem to have run in the same problem as you did. Here is a little
program to show my point:



#include<iostream>
#include<algorithm>
#include<vector>



//global data base, holds the number base
const static char base=10;



//global data carry, needed after all
static char carry=0;



//struct CarryChar, functor for use in STL algorithm transform()
//Note how ::carry is used to address the global carry. Remove
//the :: and the local CarryChar.carry is used instead. But you
//cannot get the value AFTER the transform() algorithm, just when
//you need it most
struct CarryChar
{

//data member carry, holds the carry after addition
char carry;

//constructor
CarryChar():carry(0){::carry=0;}

//operator bool, Used once to see if there is a push_back needed
operator bool(){return::carry;}

//operator(), return sum of two characters, stores carry in carry
char operator()(const char a,const char b)
{
char c=a+b+::carry;
::carry=0;
if(c>=base){c-=base;::carry=1;}
return c;
}};





//struct LongInt, holds an expanding vector<char> and uses CarryChar
struct LongInt
{

// //data member carrychar. Functor

//data member v, vector of characters
vector<char>v;

//constructor
LongInt(char a){do v.push_back(a%base);while((a/=base)!=0);}

//operator +=, adds two LongInts and swaps the vectors
LongInt&operator+=(LongInt&a)
{
v.resize(a.v.size());
CarryChar carrychar;
transform(v.begin(),v.end(),a.v.begin(),v.begin(),carrychar);
//Here is where you need the value of carrychar.carry but it
//is outside your reach. You must use the global ::carry to
//achieve the desired result.
if(carrychar)v.push_back(1);
v.swap(a.v);
return*this;
}};



//operator<< outputs a LongInt object (to the screen)
ostream&operator<<(ostream&a,const LongInt&b)
{
copy(b.v.rbegin(),b.v.rend(),ostream_iterator<int>(a));
return a;
}





//main. Note how LongInt(0) is forced cout<< to produce 0,1,1,2,3,5,8,13...
int main()
{
LongInt a(0);
LongInt b(1);
cout<<a<<'\n';
for(int c=0;c<20;c++)cout<<(a+=b)<<'\n';
return 0;
}
 
A

Alex Vinokur

Agent Mulder said:
LongInt&operator+=(LongInt&a)
{
v.resize(a.v.size());
CarryChar carrychar;
transform(v.begin(),v.end(),a.v.begin(),v.begin(),carrychar);
//Here is where you need the value of carrychar.carry but it
//is outside your reach. You must use the global ::carry to
//achieve the desired result.

Exactly. This situation caused me to ask my question.
if(carrychar)v.push_back(1);
v.swap(a.v);
return*this;
}};
[snip]

--
=====================================
Alex Vinokur
mailto:[email protected]
http://mathforum.org/library/view/10978.html
news://news.gmane.org/gmane.comp.lang.c++.perfometer
=====================================
 
M

Micah Cowan

Alex Vinokur said:
Functor-parameters in the for_each() and transform() algorithms are passed by value.

Might it make sense to have also algorithms for_each2() and transform2()
which pass Functor-parameters by non-const reference?


Here is some program which demonstrates probable necessity
in such forms of for_each() and transform().

class Foo
{
friend ostream& operator<< (ostream& os, const Foo& ioo);

private :
int sum_;

int operator() (int n1, int n2)
{
sum_ += n1;
sum_ += n2;
return (n1 + n2);
}

void operator() (int n)
{
sum_ += n;
}

Hm. Rather than suggest dangerous versions of for_each() and
transform(), why not rather declare sum_ as mutable, allowing it
to be changed even when the Foo object was declared as const?
 
H

Howard Hinnant

Alex Vinokur said:
Functor-parameters in the for_each() and transform() algorithms are passed by
value.

Might it make sense to have also algorithms for_each2() and transform2()
which pass Functor-parameters by non-const reference?

Another solution is to explictly specify the template arguments to the
algorithms. In this way you can transform the call-by-value into a
call-by-reference. For example:

transform
<
vector<int>::iterator,
vector<int>::iterator,
vector said:
(x.begin(), x.end(), y.begin(), vect_.begin(), *this);

....

sum_ = 295 (after transform)
{ vect_ = 11 22 33 44 55 60 70 }

Unfortunately the above code isn't portable. Metrowerks CodeWarrior is
the only product which has publicly stated this code is guaranteed to
work (as a conforming extension to the standard).

A defect report was submitted years ago on this issue:

http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#92

The consensus of the lwg is to make code such as that shown above
illegal. The state of a stateful function object does not have to be
respected. Although issue 92 is not yet set in stone, just last week
the issue was moved one step closer to being official. It will become
official (DR status) this October unless someone not only makes a lot of
noise, but is also successful at convincing others that this issue is
not being settled correctly.

Years ago I argued for the state of stateful function objects to be
respected. But I was unsuccessful in convincing others to my viewpoint.
So I settled for just allowing my own customers this behavior.

If you feel that you would like to see this behavior standardized, you
will need to go to comp.std.c++ and speak long and eloquently. You
should reference issue 92 directly, and you might also reference an
excellent article written by Klaus Kreft and Angelika Langer.

http://www.cuj.com/documents/s=8000/cujcexp1812langer/

Otherwise you can build the extra level of indirection into the functor
you pass. For example you could create a FooRef class that takes a Foo
and stores a pointer to it.

-Howard
 
A

Alex Vinokur

Howard Hinnant said:
Alex Vinokur said:
Functor-parameters in the for_each() and transform() algorithms are passed by
value.

Might it make sense to have also algorithms for_each2() and transform2()
which pass Functor-parameters by non-const reference?
[snip]

If you feel that you would like to see this behavior standardized, you
will need to go to comp.std.c++ and speak long and eloquently.

[snip]

I have sent my original posting to comp.std.c++ .


--
=====================================
Alex Vinokur
mailto:[email protected]
http://mathforum.org/library/view/10978.html
news://news.gmane.org/gmane.comp.lang.c++.perfometer
=====================================
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top