 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Peter C. Chapin Guest
|
Posted: Tue Aug 03, 2004 11:24 am Post subject: Modifying a sequence with std::for_each? |
|
|
Is it well defined to pass std::for_each a function that modifies its
argument (for example, a function that takes an argument by reference)? Both
g++ v3.3.1 and MSVC++ v7.1 will compile such usage but the programs bomb when
they run. For example, when the g++ produced program runs, I get a SIGSEGV
the first time std::for_each attempts to use the given function.
Peter
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Potter Guest
|
Posted: Wed Aug 04, 2004 10:14 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
On 3 Aug 2004 07:24:29 -0400, "Peter C. Chapin" <pchapin (AT) sover (DOT) net>
wrote:
| Quote: | Is it well defined to pass std::for_each a function that modifies its
argument (for example, a function that takes an argument by reference)?
|
Yes.
| Quote: | Both
g++ v3.3.1 and MSVC++ v7.1 will compile such usage but the programs bomb when
they run. For example, when the g++ produced program runs, I get a SIGSEGV
the first time std::for_each attempts to use the given function.
|
G++ works fine. Post the bad code so that we can show you your error.
John
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
john dibling Guest
|
Posted: Wed Aug 04, 2004 12:11 pm Post subject: Re: Modifying a sequence with std::for_each? |
|
|
On 3 Aug 2004 07:24:29 -0400, "Peter C. Chapin" <pchapin (AT) sover (DOT) net>
wrote:
| Quote: |
Is it well defined to pass std::for_each a function that modifies its
argument
|
No, it is not. for_each() takes Input Iterators, which cannot be
assigned through.
Use transform() instead.
- john dibling
dibsatdiblingdotcom
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Peter C. Chapin Guest
|
Posted: Wed Aug 04, 2004 1:50 pm Post subject: Re: Modifying a sequence with std::for_each? |
|
|
John Potter <jpotter (AT) falcon (DOT) lhup.edu> wrote in news:FfLPc.24008$iK.11057
@newsread2.news.atl.earthlink.net:
| Quote: | G++ works fine. Post the bad code so that we can show you your error.
|
My program was attempting to modify a string literal (indirectly) and
this
was the problem. String literals contain constant characters despite
the fact
that you can put their address into a pointer to non-const character.
Peter
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
red floyd Guest
|
Posted: Thu Aug 05, 2004 3:17 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
john dibling wrote:
| Quote: | On 3 Aug 2004 07:24:29 -0400, "Peter C. Chapin"
wrote:
Is it well defined to pass std::for_each a function that modifies its
argument
No, it is not. for_each() takes Input Iterators, which cannot be
assigned through.
Use transform() instead.
|
Josuttis lists for_each() as a modifying algorithm (pp 325,326), and
notes that it's more efficient for modify in place than transform(), due
to the lack of the assignment.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ali Cehreli Guest
|
Posted: Thu Aug 05, 2004 10:39 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
On Wed, 04 Aug 2004 05:11:18 -0700, john dibling wrote:
| Quote: | On 3 Aug 2004 07:24:29 -0400, "Peter C. Chapin"
wrote:
Is it well defined to pass std::for_each a function that modifies its
argument
No, it is not. for_each() takes Input Iterators, which cannot be
assigned through.
|
This is incorrect. for_each works with any iterator-like type
that supports
operator!=
operator++
operator*
As long as the operation (functor) passed to for_each accepts
what operator* returns, it will work fine.
| Quote: | Use transform() instead.
|
That works too.
Ali
P.S. If I am not mistaken, transform is the only option when it
is needed to be guaranteed that the functor is not copied. I
can't remember that subtle difference between the two but I think
it was about for_each not giving the guarantee that the functor
will not be copied. And I think transform gives the guarantee
that the functor will not be copied. I am confused now. :)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Peter C. Chapin Guest
|
Posted: Thu Aug 05, 2004 10:39 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
john dibling <dibs (AT) dibling (DOT) com> wrote in
news:i2hvg092j559hjsm9l4i3566slo4k6nmeb (AT) 4ax (DOT) com:
| Quote: | Is it well defined to pass std::for_each a function that modifies its
argument
No, it is not. for_each() takes Input Iterators, which cannot be
assigned through.
|
Hmm. Isn't this more of a restriction on the implementation of for_each than
it is on the program that uses for_each? The for_each template might only use
the facilities of input iterators, but there's no reason I can't provide it
with, say, random access iterators. In other words, if I *know* that I'm
giving for_each a sequence that can be modified, can I expect for_each to
behave itself if I also give it a function that tries to modify that
sequence?
Peter
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
llewelly Guest
|
Posted: Thu Aug 05, 2004 10:51 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
John Potter <jpotter (AT) falcon (DOT) lhup.edu> writes:
| Quote: | On 3 Aug 2004 07:24:29 -0400, "Peter C. Chapin"
wrote:
Is it well defined to pass std::for_each a function that modifies its
argument (for example, a function that takes an argument by reference)?
Yes.
|
But 25.1.1 says the arguments to for_each are InputIterators, and
24.1.1 says InputIterators are not assignable.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
James Hopkin Guest
|
Posted: Thu Aug 05, 2004 10:54 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
john dibling <dibs (AT) dibling (DOT) com> wrote
| Quote: | On 3 Aug 2004 07:24:29 -0400, "Peter C. Chapin"
wrote:
Is it well defined to pass std::for_each a function that modifies its
argument
No, it is not. for_each() takes Input Iterators, which cannot be
assigned through.
Use transform() instead.
|
Sorry, that's just wrong.
InputIterator is the *minimum* requirement for for_each. True, if you
pass input iterators which aren't forward iterators (e.g.
istream_iterator), you can't use a modifying functor.
However, there's no problem passing forward iterators and modifying
elements in the range.
Use transform if you *don't* want to modify in place.
James
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Francis Glassborow Guest
|
Posted: Thu Aug 05, 2004 2:08 pm Post subject: Re: Modifying a sequence with std::for_each? |
|
|
In article <86smb241ls.fsf (AT) Zorthluthik (DOT) local.bar>, llewelly
<llewelly.at (AT) xmission (DOT) dot.com> writes
| Quote: | John Potter <jpotter (AT) falcon (DOT) lhup.edu> writes:
On 3 Aug 2004 07:24:29 -0400, "Peter C. Chapin"
wrote:
Is it well defined to pass std::for_each a function that modifies its
argument (for example, a function that takes an argument by reference)?
Yes.
But 25.1.1 says the arguments to for_each are InputIterators, and
24.1.1 says InputIterators are not assignable.
|
No, it requires that they be at least InputIterators it does not (quite
correctly) require that they are only that. What the Standard does is
require that for_each() work even if the iterator you provide only meets
the minimum requirements for and InputIterator.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Dibling Guest
|
Posted: Fri Aug 06, 2004 10:22 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
| Quote: | Josuttis lists for_each() as a modifying algorithm (pp 325,326), and
|
The book you quote is 5 years old, and since that there has been a new
C++ standard published. In that standard, for_each() is listed in
section 25.1.1 under "Non-Modifying Sequence Operations." So,
Josuttis is out-of-date with regards to for_each().
Even still, my Austern book (Generic Programming and the STL) is also
copyright 1999, same as Josuttis, and Austern lists for_each() as
non-mutating as well. In addidtion, Austern describes the
non-mutating algorithms as a whole like this:
"This chapter describes basic STL algorithms that operate on a range
of iterators without changing the elements those iterators point to."
(p.199)
I can't explain or understand why the two books dissagree with each
other regarding for_each(), becasue although I don't know the J. book
well, I know it is respected. But whatever. The authoritative source
is the Standard, and it says for_each() is a non-modigying sequence
operation. Therefore any operation passed to for_each() which
modifies the sequence is ill-formed.
| Quote: | notes that it's more efficient for modify in place than transform(), due
to the lack of the assignment.
|
The Standard says that the complexity for :
for_each(first, last, f)
is "exactly last-first applications of f." Furthermore, it says that
the complexity for:
transform(first, last, result, op)
is exactly last-first applications of op. So the complexity of both
functions is the same. I don't see what extra assignment J. could be
talking about, but I don't think he was right.
- John Dibling
dibsatdiblingdotcom
- John Dibling
dibsatdiblingdotcom
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Dibling Guest
|
Posted: Fri Aug 06, 2004 10:25 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
| Quote: | for_each works with any iterator-like type that supports
[snip] |
This is true, and what you have just described is what the Standard
defines an an InputIterator. (24.1.1)
| Quote: | P.S. If I am not mistaken, transform is the only option when it
is needed to be guaranteed that the functor is not copied. I
can't remember that subtle difference between the two but I think
it was about for_each not giving the guarantee that the functor
will not be copied. And I think transform gives the guarantee
that the functor will not be copied. I am confused now.
|
Instead of focusing on just the minutiae technical details of
for_each()'s and transform()'s requirements, focus instead on the
big-picture intent of the two algorithms. for_each() is intended to
pass each element in a collection to a function, f. transform() is
intended to create new elements in a collection based on the elements
that are already in some collection. The differences are not subtle,
they are gross.
Its possible to use for_each to do some kind of accumulation, by
repeatadly modifying the functor that is passed to for_each(). If
this is what you intend to do however, you might consider using
accumulate() instead, if for no other reason that for the sake of
semantic clarity. But you have to pick your spots - accumulate() is
right sometimes, for_each() other times.
Note however that it is *not* possible to use for_each() to modify the
original sequence. This is what the OP's original post was all about
(at least, that's what the sublect line indicates). And this is my
main point in my original reply to the OP.
It is also possible to use transform() to not just go from one
collection to another -- you can also do an in-place change in a
source collection. This functionality is guaranteed by the standard:
[25.2.3] :
transform(InputIterator first, InputIterator last,
OutputIterator result, UnaryOperation op)
: :
NOTE: result may be equal to first [...]
For example, suppose you have a collection of ints. If you wanted to
run throught the collection and multiply each value by 3, putting the
new value back in the same collection in place of the original, you
can use transform, like this:
(Note: TestVals could also be a vector, list, or what have you)
[FILE: main.cpp]
#include <cstdlib>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int TestVals [] = {1, 2, 3, 4, 5};
static const size_t NumTestVals =
sizeof(TestVals)/sizeof(TestVals[0]);
template<class T>
struct dump
{
void operator()(const T&t) const { cout << t << endl; }
};
int main()
{
cout << "original collection:" << endl;
for_each(&TestVals[0],&TestVals[NumTestVals],dump
transform(&TestVals[0],&TestVals[NumTestVals],&TestVals[0],bind1st(multiplies<int>(),3));
cout << "resulting collection:" << endl;
for_each(&TestVals[0],&TestVals[NumTestVals],dump
return 0;
}
[END FILE: main.cpp]
- John Dibling
dibsatdiblingdotcom
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Dibling Guest
|
Posted: Fri Aug 06, 2004 10:26 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
| Quote: | Isn't this more of a restriction on the implementation of for_each than
it is on the program that uses for_each?
|
My claim is that it is both.
| Quote: | if I *know* that I'm giving for_each a sequence that can be modified, can I expect for_each to
behave itself if I also give it a function that tries to modify that
sequence?
|
In order for your assertion to be correct, you must know something
about the implementation of for_each(). It goes against the idea of
even having a Standard at all to make the programmers know
implementation details of the interfaces defined in the Standard. In
other words, these things are supposed to be black boxes. The
interface defines the iterators as InputIterators. In order for your
code to work, you must know that for_each() doesn't use them as just
InputIterators, becasue if it did, they could be const.
For instance, the Standard says that for_each() must iterators that
can be used as InputIterators. Well, InputIterators can't be assigned
through, so you could pass in a const T*, and that is an
InputIterator. Similarly, there is nothing in the standard that says
for_each() can't explicitly convert whatever iterator you pass in to a
const version of that iterator. Of course you can't assign through a
const pointer,so any attempt to use that implemenation of for_each to
modify the collection would not compile. Now of course, you aren't
going to win any awards for wrinting a for_each() like that, but
that's wuite beside the point. The point is it is standard compliant
to do so.
- John Dibling
dibsatdiblingdotcom
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John Dibling Guest
|
Posted: Fri Aug 06, 2004 10:26 am Post subject: Re: Modifying a sequence with std::for_each? |
|
|
| Quote: | InputIterator is the *minimum* requirement for for_each.
|
Not only is it the minumum requitrement, it is the only requirement
regarding the iterators.
| Quote: | However, there's no problem passing forward iterators and modifying
elements in the range.
|
From a conceptual point of view, the problem is that the Standard
defines for_each() as a non-Mutating sequence operation, but you have
used it to mutate the sequence. It is true that in most cases you can
contort your code to get for_each to mutate the sequence just fine.
But the subject of the OP was: "Modifying a sequence with
for_each()?". According to the Standard, this is an ill-defined use
of for_each().
Here's some sample code that illustrates what I believe the OP is
trying to do:
[FILE: main.cpp]
#include <cstdlib>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int TestVals [] = {1, 2, 3, 4, 5};
static const size_t NumTestVals =
sizeof(TestVals)/sizeof(TestVals[0]);
template<class T>
struct dump
{
void operator()(const T&t) const { cout << t << endl; }
};
template
struct multiply_assign : public unary_function<T,void>
{
multiply_assign(T mul) : m_mul(mul) {};
void operator()(T& r) const { r *= m_mul; }
private:
T m_mul;
};
int main()
{
cout << "original collection:" << endl;
for_each(&TestVals[0],&TestVals[NumTestVals],dump
// TEST LINE
int * pFirst = &TestVals[0], * pLast = &TestVals[NumTestVals];
for_each(pFirst,pLast,multiply_assign<int>(3));
cout << "resulting collection:" << endl;
for_each(&TestVals[0],&TestVals[NumTestVals],dump
return 0;
}
[END FILE: main.cpp]
The multiply_assign functor is used by for_each to modify the
sequence. My claim is that this is not a Standard-friendly way to use
for_each, and here's why. Since for_each takes InputIterators, it
should be possible to send const pointers to for_each. If the way
for_each is being used is Standard-compliant, the new code should
still compile. But if you change the test line from:
// TEST LINE
int * pFirst = &TestVals[0], * pLast = &TestVals[NumTestVals];
....to...
// TEST LINE
const int * pFirst = &TestVals[0], * pLast =
&TestVals[NumTestVals];
.... the code no longer compiles.
| Quote: | Use transform if you *don't* want to modify in place.
|
You can use transform to modify in place. This functionality is
guaranteed by the Standard. I discuss this in greater depth in my
reply to another post, and there is runnable sample code there as
well.
Now, I must also point out that in re-reading the OPs original post,
"Is it well defined to pass std::for_each a function that modifies its
argument "
This question is different from the subject line. I t*is* well formed
to modify the 3rd paramater to for_each(). You would do this to sum
up a vector of ints, for instance, and this is fine. But what you
cannot do is pass a functor to for_each() that changes the values in
the sequence. I hope I haven't answered a question that the OP didn't
intend to ask...
- John Dibling
dibsatdiblingdotcom
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Francis Glassborow Guest
|
Posted: Fri Aug 06, 2004 3:16 pm Post subject: Re: Modifying a sequence with std::for_each? |
|
|
In article <thi4h0h8cu0mhdv3uv7sbb17he79sp8fn6 (AT) 4ax (DOT) com>, John Dibling
<jdibling (AT) yahoo (DOT) com> writes
| Quote: | The book you quote is 5 years old, and since that there has been a new
C++ standard published. In that standard, for_each() is listed in
section 25.1.1 under "Non-Modifying Sequence Operations." So,
Josuttis is out-of-date with regards to for_each().
Even still, my Austern book (Generic Programming and the STL) is also
copyright 1999, same as Josuttis, and Austern lists for_each() as
non-mutating as well. In addidtion, Austern describes the
non-mutating algorithms as a whole like this:
"This chapter describes basic STL algorithms that operate on a range
of iterators without changing the elements those iterators point to."
(p.199)
I can't explain or understand why the two books dissagree with each
other regarding for_each(), becasue although I don't know the J. book
well, I know it is respected. But whatever. The authoritative source
is the Standard, and it says for_each() is a non-modigying sequence
operation. Therefore any operation passed to for_each() which
modifies the sequence is ill-formed.
|
One (Nico's) is written by a programmer (albeit one who is an active and
knowledgeable member of WG21) who knew from experience that for_each()
usually can modify the elements. Matt Austern comes from the
implementation side of the C++ community and as such is far more aware
of the exact wording of the C++ Standard.
In theory using for-each() with a modifying predicate is an error, in
practice it usually works because making it always fail requires, I
think, an inefficient implementation of for_each().
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|