Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Manual Loops vs STL Algorithms in the Real World

66 views
Skip to first unread message

Scott Meyers

unread,
Jul 27, 2005, 4:08:55 AM7/27/05
to
It's my job to know about C++, but I'm not a C++ programmer. That is,
producing C++ source code for nontrivial projects is not how I spend my
time. Yet I routinely offer advice to C++ programmers about how to write
good code, and there is some evidence that some of them listen to me. From
time to time, I need a reality check. This is one such time.

I got the following email query from a reader. It boils down to asking
whether it's really reasonable to expect C++ programmers to use STL
algorithms instead of writing their own loops, a topic I address in my
"Effective STL," where I argue that it often is. But the reader puts the
question so much better. It's long, but very much worth reading, IMO:

One of the things that I have noticed in your Effective STL book,
Stroustup's books, several of Herb Sutter's books, Bruce Eckel's books,
and Matt Austern's "Generic Programming") is that most (if not all) of
the C++ writer/developers I respect are encouraging their readership to
embrace Generic Programming, particularly with regard to using algorithms
-- especially in the area of replacing manual looping with algorithm
calls. Now, you don't have to convince me that this is a good idea.
I've read the discussions, and so far I am in agreement with the majority
-- at least, I *want* to be. I am currently working on a project where I
use a lot of standard library containers. So in the interest of "doing
things better", I tried to use algorithms instead of manual loops. I had
a really difficult time making this work. In fact, the features I tried
to implement with algorithms were different enough from *any* of the
examples I found, that I finally just gave up and went back to manual
looping. Now, I did not give up easily (as I generally don't). I put
days (weeks?) into trying to figure this out. I don't remember the exact
problem I was trying to solve (it was a while back), but I do recall that
the manual loop wasn't all that complicated. I also recall being
frustrated that I spent so much time on it and *still* was unable to make
an algorithm do the job. The feeling I came away with from this was
that, sure, using algorithms *sounds* like the right thing to do, but in
practice I can write in 30 minutes with a manual loop something I
couldn't figure out in *days* using an algoritnm. Function Objects
seemed to overcomplicate the code, for one thing. For another thing, I
just couldn't get the algorithm to do the job I wanted done.

Herb Sutter recommends using lambda expressions from the Boost library to
automatically generate function objects and make algorithm calls cleaner.
I have looked a little at that (and will look in more detail later), and
it shows promise. However, I am a little skeptical because of my past
experience. I'm no genius, but I consider myself to be a reasonably
above average programmer. I have a Bachelor's in CS from 10 years ago,
have been working in the industry ever since. I'm no newbie. I have a
good grasp on OOP, but am also aware that it is not a panacea. I have
written a fair bit at a low level in assembly and C, but have also
written a lot of Perl, Java, and of course C++. I've done a fair bit of
studying on software design (including Design Patterns, etc). One of the
things I *like* about C++ is that it is a multi-paradigm language --
since I'm a pretty pragmatic person, and I believe in knowing many things
well, and using the right tool/method for the right job. Thus you could
understand why I want to master the whole generic programming thing.

Can you point me to some practical software that uses generic programming
in C++ extensively (perhaps a good open-source project)? Even better
would be a source for a ton of examples of the nature: "Here is how a
programmer would probably write this using a loop. And here is the same
solution using an algorithm." I realize that you have a number of
similar examples in your Effective STL book. I guess I'm looking for
something more extensive. I obviously seem to be missing something,
since so many of the "top dogs" are leaning this direction. But for some
reason I'm not getting it. Whatever the source, it would be *really*
nice to find an entire resource devoted to "you *used* to do it this way,
using algorithms you do it *this* way". So far I have found no such
resource. Item 43 of "Effective STL" (as well as some of the other
items) were helpful, but I guess I just need something more. Do you have
any suggestions?

>From my perspective, there are two questions here. One is, as I said,
whether it is practical to replace many manual loops with algorithm calls.
The other is where people can find open-source examples of real systems
that actually do it.

The first question, especially, should really be answered only by people
who spend a lot of time working on real C++ systems. I welcome comments on
both questions, as I hope to be able to enlighten not only my reader, but
also myself.

Thanks,

Scott

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Mirek Fidler

unread,
Jul 27, 2005, 6:15:17 AM7/27/05
to
>>From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.

IME, no.

IME, and I know most people will disagree with me, the most effective
solution is to avoid node base container and iterators and use indicies
everywhere.

Makes coding trivial, short and unlikely to be prone to hard to catch
errors like silently invalidated iterators or iterators going out of
range (ok, I know that some STL implementation provide runtime checks,
but thing is that these kinds of error are much less likely to happen
with indicies).

I believe that all that "for_each" madness was cause by two factors:

- it looks "cool" and "advanced"

- it is annoying to create for loop for STL containers as iterators
syntax is usually way too ugly

for(std::vector<std::string>::iterator it = c.begin(); it != c.end(); it++)

I guess this is something that nobody really likes... (I think that at
the moment "auto" extension is introduced, all that "for_each" stuff
will be silently forgotten in year or two).

As for me, I prefer easy to maintain variant

for(int i = 0; i < c.GetCount(); i++)


Mirek

Maciej Sobczak

unread,
Jul 27, 2005, 6:30:20 AM7/27/05
to

Yes, the issues you raise are quite fundamental.
Talking from the practical perspective:

I find myself using STL algorithms in those situations where they are
*really* obvious. Copying, sorting or filling arrays, finding min and
max of a given sequence, etc. - these are things that you can do by just
picking the tool from the box and using it where it immediately fits.
(or, getting the big nail and just smashing it in with one stroke - this
might be a good comparison as well)

Notice important words above: "immediately", "one stroke". This is the
border behind which the whole idea breaks - and also the problem of your
reader, I think. If the tool does not fit immediately, I just don't
bother tinkering with it. This applies most often to std::for_each, for
example - any time I thought of using it, it would require dragging some
binders or composers as well. Yes, it is cool to write funky code (but
without *true* lamda in the language it is still just pretending ;) ),
but... in practice we don't write code only for ourselves. Working in a
team means writing for others as well. I would not like to *have* to
deal with somebody else's funkiness, so I don't impose mine on others -
I believe it works very well in a long run.

If the tool does not fit immediately out of the box, I don't bother.
I see no reason for your reader to get inferiority complexes because of
that as well.

Of course, this should not prevent anybody from reading and learning STL
in its entirety - knowing the tools and deciding when to use them (or
not) is always much better than not using the tools just because of not
knowing them.


--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/

Thomas Richter

unread,
Jul 27, 2005, 8:36:48 AM7/27/05
to
Hi Scott,

/* snip */

> >From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.
> The other is where people can find open-source examples of real systems
> that actually do it.

> The first question, especially, should really be answered only by people
> who spend a lot of time working on real C++ systems. I welcome comments on
> both questions, as I hope to be able to enlighten not only my reader, but
> also myself.

As for me, I program in C++ for more than five years by now, for
industry and for academic purposes, and I still prefer manual
implementation; mainly, because most algorithms found in the STL are
so trivial that I do not win anything when using them; the problem can
be solved by a similar trivial manual loop, and I prefer the explicit
form over the "hidden" algorithmic approach; the manual implementation
typically fits into three to five lines, and is IMHO more readable and
down to earth, and even easier to debug. A corresponding implementation
using algorithms is often of the same size, so I don't win anything.

The only exception where I have made use of the STL is the area of
sorting algorithms; a merge or a quick sort implementation requires
more than a trivial amount of lines, and you do make mistakes there,
so the STL version is really more convenient. If the problem at hand
would have been for industrial applications, I might have written
my own code here as well should I have found some weaknesses of the
STL version (eg. critical timing, etc.).


So long,
Thomas

Francis Glassborow

unread,
Jul 27, 2005, 10:50:48 AM7/27/05
to
In article <3kovmgF...@individual.net>, Mirek Fidler <c...@volny.cz>
writes

>I believe that all that "for_each" madness was cause by two factors:
>

Was that really what the quoted person was talking about? I got the
impression that what he meant was adding algorithms rather than using
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

Harald Luessen

unread,
Jul 27, 2005, 11:26:15 AM7/27/05
to
On 27 Jul 2005 Scott Meyers wrote:
>I got the following email query from a reader. It boils down to asking
>whether it's really reasonable to expect C++ programmers to use STL
>algorithms instead of writing their own loops, a topic I address in my
>"Effective STL," where I argue that it often is. But the reader puts the
>question so much better. It's long, but very much worth reading, IMO:

I remember some months ago there was a long thread and discussion
in this newsgroup about this topic, algorithms or loops. What I
have observed as a lurker is: There were questions about the
meaning and the right way to use the algorithms. How did the
functional objects look like? Their source was not at the same
place as the one-liner algorithm. And so on. May be it just
doesn't look very common and custom. That is part of the problem.

What everybody in the thread did first was to show the corresponding
loop, a three-liner, and nobody asked about its meaning. Everybody
understood immediately. Imagine the not so skilled collegues who
may have to understand and change your code. That is part of the
answer to your questiion.

Harald

Russell Hind

unread,
Jul 27, 2005, 11:23:37 AM7/27/05
to
Scott Meyers wrote:
>
> The first question, especially, should really be answered only by people
> who spend a lot of time working on real C++ systems. I welcome comments on
> both questions, as I hope to be able to enlighten not only my reader, but
> also myself.
>

I think the learning curve for function objects is quite high. I've
only browsed boost::lambda docs as Borland's bcc32 5.6.4 can't compile
it which we currently use, and I think it does help with creating
in-place function objects for use in algorithms, but is subtly different
enough from the manual loop syntax that it isn't such a simple learning
curve. I'd like to be able to write something like:

for_each(container.begin(), container.end(), _1->function());

But I don't believe that is possible without language support. I
believe the lambda expression would be

for_each(container.begin(), container.end(), _1 ->* &myclass::function);

(But not sure as can't check with compilation).

We are currently stuck with boost::bind which gives us

for_each(container.begin(), container.end(), bind(&myclass::function, _1));

I took up to a year for me to be confident enough with bind to decide it
may be better than a hand-written loop. But there are cases where it
just looks complicated. e.g. some of our data structures have a
'get_name' member function with returns a string. We want to find which
one has a particular name and the bind expression comes out to be:

find_if(m_ExistingExcipients.begin(), m_ExistingExcipients.end(),
bind(equal_to<string>(), bind(&Excipient_c::GetDescription,
_1), Name));

The lambda equivelant of this may be simpler, but this certainly isn't
obvious to people who are un-familiar with this expression and its hard
to convice them that it is better to use this than a hand-written loop,
especially with the current errors you get from the compiler if you make
a mistake in your expression.

Sorting them by description gives us

std::sort(Excipients.begin(), Excipients.end(),
bind(less<string>(), bind(&Excipient_c::GetDescription, _1),
bind(&Excipient_c::GetDescription, _2)));

Eric Niebler's BOOST_FOREACH macro has been accepted and will hopefully
appear in boost-1.34 which may well simplify things as with that, the
above could be

shared_ptr<Excipient_c> Excipient;
BOOST_FOREACH(Excipient, Excipients)
{
if (Excipient->GetDescription() == Name)
{
break;
}
}

Which is much more readable IMHO even if it is using a macro. The
disadvantage of BOOST_FOREACH is that I don't think you can get access
to the iterator if Excipient which the std::algorithms and hand-coded
loops give you.

I try to use algorithms now where possible (but only using bind, not
lambda yet until Borland finally upgrade their compiler) and lambda does
simplify things but I still believe that lambda and bind have too steep
a learning curve to be immediately attractive. As mentioned, the lambda
library is very powerful, but the syntax is similar yet different to
what you'd write in a hand-written loop that it isn't immediately
obvious what you need to change from the hand-written version to turn it
in to a lambda expression.

Another library not mentioned here is boost::range which can simplify
the expressions slightly for both hand-written loops and algorithms as
you don't need both begin and end.

Cheers

Russell

w...@seed.net.tw

unread,
Jul 27, 2005, 11:17:54 AM7/27/05
to
Scott Meyers wrote:
>..

>whether it is practical to replace many manual loops with
>algorithm calls.

Let's say, If I already know how to program in C++, why
bother spending longer time to learn STL, more complex than the
language itself and contains many exceptional rules, no less
pitfalls but less flexibility, hard to understand compiler error
report.., and vaguely documented (for my encountered problems).

So it is not practical to me for now and the near future.

IMHO, template modifies the language. It should be mostly hidden.
The wording "replace many manual loops with algorithm calls."
looked to me is changing the language.

David Abrahams

unread,
Jul 27, 2005, 11:23:12 AM7/27/05
to
Scott Meyers <Use...@aristeia.com> writes (on behalf of one of his readers):

> ....most (if not all) of the C++ writer/developers I respect are


> encouraging their readership to embrace Generic Programming,
> particularly with regard to using algorithms -- especially in the
> area of replacing manual looping with algorithm calls. Now, you
> don't have to convince me that this is a good idea. I've read the
> discussions, and so far I am in agreement with the majority -- at
> least, I *want* to be.

That certainly sounds familiar ;-)

> I am currently working on a project where I
> use a lot of standard library containers. So in the interest of "doing
> things better", I tried to use algorithms instead of manual loops. I had
> a really difficult time making this work. In fact, the features I tried
> to implement with algorithms were different enough from *any* of the
> examples I found, that I finally just gave up and went back to manual
> looping.

This part is interesting. It isn't the usual complaint that you hear
about the algorithm interface, namely, that it's cumbersome to use.
There's some validity to _that_ assertion, which is why my next
library (and the MPL, incidentally) uses an interface that accepts the
entire range to be operated on in one object instead of working on
"two separate iterators." However, it's also the reason Boost
accepted Eric Niebler's FOREACH macro
(http://boost-sandbox.sourceforge.net/vault/index.php?directory=eric_niebler,
including docs). [1]


> Now, I did not give up easily (as I generally don't). I put
> days (weeks?) into trying to figure this out. I don't remember the exact
> problem I was trying to solve (it was a while back), but I do recall that
> the manual loop wasn't all that complicated. I also recall being
> frustrated that I spent so much time on it and *still* was unable to make
> an algorithm do the job.

Often the algorithms you need are lurking there, but it can be
difficult to identify them unless you're really familiar with what's
available and how to use it. That said, unless your reader can
remember what s/he was trying to do, it's hard to say. The STL
doesn't claim to cover everything you might want to do with sequence
traversal, though the aglorithm suite does cover most of the
fundamentals.

> The feeling I came away with from this was
> that, sure, using algorithms *sounds* like the right thing to do, but in
> practice I can write in 30 minutes with a manual loop something I
> couldn't figure out in *days* using an algoritnm. Function Objects
> seemed to overcomplicate the code, for one thing.

Unless you have a good library at your disposal to generate them with,
they can.

> For another thing, I just couldn't get the algorithm to do the job
> I wanted done.

....which seems to be the crux of your reader's problem. So we need to
know what the job was.

> Herb Sutter recommends using lambda expressions from the Boost library to
> automatically generate function objects and make algorithm calls cleaner.
> I have looked a little at that (and will look in more detail later), and
> it shows promise. However, I am a little skeptical because of my past
> experience.

What past experience? Your reader goes on to talk about his or her
qualifications and openness to ideas, but doesn't tell us what
happened.

Anyway, for what it's worth: I sometimes find Boost.Lambda
frustrating. The library is going on 5+ years old and is showing its
age. Heck, the Boost/TR1 Bind library, which is less powerful
overall, is much easier to use in some ways that haven't yet made it
into Boost.Lambda (your reader should try using Bind). We've learned
a great deal about how to do these things in C++ since it was written,
and I and quite a few others have been waiting eagerly for the
so-called "Lambda/Phoenix Merger"
(http://article.gmane.org/gmane.comp.lib.boost.devel/128254) which
should be a vast improvement.

<snip>

> One of the things I *like* about C++ is that it is a
> multi-paradigm language -- since I'm a pretty pragmatic person,
> and I believe in knowing many things well, and using the right
> tool/method for the right job. Thus you could understand why I
> want to master the whole generic programming thing.
>
> Can you point me to some practical software that uses generic programming
> in C++ extensively (perhaps a good open-source project)?

Well, the reader shouldn't equate generic programming with STL
algorithms. That's a good place to start, but GP is a much broader
idea. Just take a look at the Boost.Graph library, which is IMO one
of the most mature expressions -- and uses -- of generic programming
anywhere: a real tour de force. However, I guess s/he's probably
asking you to point to _applications_. Well, if you look at the
questions on the Boost.User mailing list you can see that *lots* of
people are using the graph library every day:
http://www.google.com/search?q=graph+site%3Alists.boost.org%2Fboost-users%2F

Also, you can look through the prototype "who's using Boost" pages and
find several mentions of the lambda library, the graph library, many
uses of Boost.Bind and Boost.Function, and countless others, all of
which are generic components and many of which are likely to be used
with STL algorithms:
http://www.boost.org/regression-logs/cs-win32_metacomm/doc/html/who_s_using_boost_.html


> Even better would be a source for a ton of examples of the nature:
> "Here is how a programmer would probably write this using a loop.
> And here is the same solution using an algorithm." I realize that
> you have a number of similar examples in your Effective STL book.
> I guess I'm looking for something more extensive. I obviously
> seem to be missing something, since so many of the "top dogs" are
> leaning this direction. But for some reason I'm not getting it.

Given the current algorithm interface and many of the available tools,
the use of an algorithm that is doing a simple linear traversal is
often not going to be easier to read than a for loop (or, maybe
better, a FOREACH loop). That's something, as I've been hinting at,
that may improve over time. However, there is a large category of
sequence algorithms that, even with today's interface and tools, I
would never want to write by hand. I've starred some fo those from
http://www.sgi.com/tech/stl/table_of_contents.html (yes, a couple of
those listed below aren't in the standard -- I've tried to avoid
starring those):


1. Non-mutating algorithms
1. for_each
2. find
3. find_if
* 4. adjacent_find
* 5. find_first_of
6. count
7. count_if
* 8. mismatch
9. equal
* 10. search
11. search_n
* 12. find_end
2. Mutating algorithms
1. copy
2. copy_n
3. copy_backward
4. Swap
1. swap
2. iter_swap
3. swap_ranges
5. transform
6. Replace
1. replace
2. replace_if
3. replace_copy
4. replace_copy_if
7. fill
8. fill_n
9. generate
10. generate_n
11. Remove
1. remove
2. remove_if
3. remove_copy
4. remove_copy_if
* 12. unique
* 13. unique_copy
14. reverse
15. reverse_copy
* 16. rotate
* 17. rotate_copy
* 18. random_shuffle
* 19. random_sample
* 20. random_sample_n
* 21. partition
* 22. stable_partition
3. Sorting
1. Sort
* 1. sort
* 2. stable_sort
* 3. partial_sort
* 4. partial_sort_copy
* 5. is_sorted
* 2. nth_element
* 3. Binary search
* 1. lower_bound
* 2. upper_bound
* 3. equal_range
* 4. binary_search
* 4. merge
* 5. inplace_merge
6. Set operations on sorted ranges
* 1. includes
* 2. set_union
* 3. set_intersection
* 4. set_difference
* 5. set_symmetric_difference
7. Heap operations
* 1. push_heap
* 2. pop_heap
* 3. make_heap
* 4. sort_heap
* 5. is_heap
8. Minimum and maximum
1. min
2. max
3. min_element
4. max_element
9. lexicographical_compare
10. lexicographical_compare_3way
* 11. next_permutation
* 12. prev_permutation
4. Generalized numeric algorithms
1. iota
2. accumulate
* 3. inner_product
* 4. partial_sum
* 5. adjacent_difference
6. power

So there's a cluster in the beginning that might be trivial enough to
write out by hand, but when I really think about it, I wouldn't want
to write most of those myself either ;-)

And that's just the expressiveness issue. When you consider the
efficiency issue, the argument for even simple algorithms gets
stronger. Libraries can do loop unrolling or more sophisticated
things [Austern98] to make those simple algorithms better than your
hand-coded loops -- if you care about efficiency, that is.

Another factor is that the STL algorithms and iterators are aimed at
the highest possible speed. Many loops in an application don't need
to be that fast. In fact, sometimes it's more important to reduce
code size, which is where components like Boost.Function and
any_iterator (here's a version from Adobe:
http://opensource.adobe.com/classadobe_1_1any__iterator.html) that use
type erasure come in handy if you want to stay within the paradigm.

> Whatever the source, it would be *really* nice to find an entire
> resource devoted to "you *used* to do it this way, using
> algorithms you do it *this* way". So far I have found no such
> resource. Item 43 of "Effective STL" (as well as some of the
> other items) were helpful, but I guess I just need something more.
> Do you have any suggestions?
>
>>From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm
> calls.

Sometimes. Usually the algorithms that are worth the trouble are much
more interesting than a loop. Better libraries and interfaces will
improve that situation a little.

> The other is where people can find open-source examples of real systems
> that actually do it.

I hope I gave you some useful pointers above.

> The first question, especially, should really be answered only by people
> who spend a lot of time working on real C++ systems.

Wow, I'm not sure whether you think library authors qualify. I wish
you had said that at the beginning! ;-)

> I welcome comments on both questions, as I hope to be able to
> enlighten not only my reader, but also myself.

Hope this helped.

Footnotes:

[1] This library was accepted long ago
(http://www.boost-consulting.com/boost/more/formal_review_schedule.html);
I'm not sure why it hasn't been added yet.

[Austern98] Matthew H. Austern, *Segmented Iterators and
Hierarchical Algorithms*, 1998. Lecture Notes In Computer
Science; Vol. 1766 Selected Papers from the International
Seminar on Generic Programming, Pages: 80 - 90,
ISBN:3-540-41090-2 http://lafstern.org/matt/segmented.pdf
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

ka...@gabi-soft.fr

unread,
Jul 27, 2005, 11:22:03 AM7/27/05
to
Scott Meyers wrote:

[...]


> It boils down to asking whether it's really reasonable to
> expect C++ programmers to use STL algorithms instead of
> writing their own loops,

Which is, IMHO, the wrong question. If a standard algorithm
fits the job, use it. If it doesn't don't try to twist it to do
something it wasn't designed to. And don't fall into the trap
of using for_each whenever none of the other algorithms fit,
just because it is cool.

On the other hand, instead of just writing a loop, it's often
worth considering writing an algorithm, and calling that, rather
than just coding the loop directly. Of course, the algorithm
may have a hand written loop. But that isn't really the point.
The point is that in the application specific code, you've given
the thing a name, and that it is usable elsewhere. And that the
subtilities of the algorithm don't get buried down (or worse,
forgotten) in the subtilities of the application specifics.

Of course, if the standard algorithm does fit, then use it. If
you need a linear search, use find or find_if, even if it means
writing a special predicate. And of course, who knows. Maybe
the predicate will be usable elsewhere, too: more than one of my
predicates has started out in an unnamed namespace, only to end
up in a header file of its own. (The CRC and MD5 accumulators
come to mind. For use with std::accumulate. But also Predicate
wrappers for std::ctype functions.)

In the end, it's just plain old functional decomposition, only
more so. Functional decomposition works. Breaking out
algorithms, predicates, etc. works even more so. Functional
decomposition involves a little more work up front. The STL way
more so. Functional decomposition saves a lot of work in the
end. The STL way more so.

[...]


> From my perspective, there are two questions here. One is, as
> I said, whether it is practical to replace many manual loops
> with algorithm calls.

It's pratical to decompose your code into the application
specific part, and the algorithms. And it seems to pay off in
the end. About the only real problem is all of the work-arounds
you need because you're constantantly dealing with two
iterators, instead of a single object. (This makes designing
new iterators particularly difficult. Because when you're not
iterating over a fixed container, you frequently don't know the
end until you get there. And it makes chaining of algorithms,
where one algorithm specifies the sequence over which the next
one iterates, unnecessarily complex as well.)

What you do have to realize is the set of useful algorithms
depends largely on the application domain. I don't know what
application domain Stepanov had in mind when he conceived the
STL, but there are a lot of algorithms in the standard that are
completely useless for me. But that's not really the point.
The point is that it is easy to write your own algorithms, and
that factoring the algorithm out of the application specific
software has definite advantages.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

wa...@stoner.com

unread,
Jul 27, 2005, 11:37:17 AM7/27/05
to

Scott Meyers wrote:
> I got the following email query from a reader. It boils down to asking
> whether it's really reasonable to expect C++ programmers to use STL
> algorithms instead of writing their own loops, a topic I address in my
> "Effective STL," where I argue that it often is. But the reader puts the
> question so much better. It's long, but very much worth reading, IMO:

The algorithms and the for loops are both tools. Use the tool that
makes sense for the task, based on your requirements, proficiency,
environment, ...

I like to use my power screwdriver, when it is handy, and it fits. It
lets me get a lot of work done quickly, and it has features (such as a
torque setting) that are useful. However, I sometimes end up using the
screwdriver on my pocketknife instead.

I use copy, find, and find_if frequently. I'm much more likely to use
a pre-written sort (or even lower_bound) than to write my own.

If the body of a for loop is a few lines long, I probably won't break
it out and use for_each, until I need to reuse it, and then, in most
cases, I end up writing a function that contains the actual loop, so
that the client code calls
ProcessRange(begin, end);
rather than
for_each(begin, end, ProcessElement);

On the other hand, if ProcessElement is useful in other contexts, the
body of ProcessRange might end up being a single for_each call.

David P. Riedel

unread,
Jul 27, 2005, 11:36:25 AM7/27/05
to
Scott

I have been programming c++ for a living for a number of years and using the STL for quite a while. I've read quite a
few of the texts which discuss how to take advantage of this code (including yours : ) . I, too, often debate this issue
and haven't decided whether some of these techniques are really worth while.

For example, I have some production code in a system I maintain which uses a map whose data type is a vector of Boost
smart pointers. With the STL containers this is trivial to set up and saves a lot of time.

When I insert something into the vector for an entry I don't want to have duplicates so I use the following code to see
if I have an existing entry before adding a new one.

--------code below

Mbr_Jrnl_Data_List& memberList = mJrnl_Data_List[newData->mJRNL_ID];

Mbr_Jrnl_Data_List::iterator i = std::find_if(memberList.begin(),
memberList.end(), boost::bind(&JRNL_Data::Matches, _1, boost::ref(newData)));

if (i == memberList.end())
memberList.push_back(newData);

--------code above

To me, this seems easier an more straight forward than writing the loop myself.

However, I have some other code where I want to extract certain records from the vector which belongs to a certain
key in my map and add those entries to a new vector. After much effort, I have the following code.

----------code below

std::remove_copy_if(i->second.begin(), i->second.end(), std::back_inserter(layoutTrans),
boost::bind
(std::not_equal_to<JRNL_Data::RequestTypeCodes>(), boost::bind(&JRNL_Data::GetRequestType, _1), JRNL_Data::e_layout_req));

--------code above

While this works and is actually fairly compact for what I need to do and did not require me to write any code other
than the to calls STL algorithms it took a lot longer to figure this out than it would have taken to write the loop
myself. I am going with this version for now because it requires less of 'my' code and also because I hope that if I
work with more of this type code I will get to be more comfortable with it. However, when someone else comes to work on
this code in the future he/she may not find this 'intuitively obvious'. I do have an extensive comment describing what
I am doing in this method.

Dave Riedel


Scott Meyers wrote:
> It's my job to know about C++, but I'm not a C++ programmer. That is,
> producing C++ source code for nontrivial projects is not how I spend my
> time. Yet I routinely offer advice to C++ programmers about how to write
> good code, and there is some evidence that some of them listen to me. From
> time to time, I need a reality check. This is one such time.
>
> I got the following email query from a reader. It boils down to asking
> whether it's really reasonable to expect C++ programmers to use STL
> algorithms instead of writing their own loops, a topic I address in my
> "Effective STL," where I argue that it often is. But the reader puts the
> question so much better. It's long, but very much worth reading, IMO:
>

snip

Howard Hinnant

unread,
Jul 27, 2005, 11:38:04 AM7/27/05
to
In article <MPG.1d50a59dd...@news.hevanet.com>,
Scott Meyers <Use...@aristeia.com> wrote:

> From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.
> The other is where people can find open-source examples of real systems
> that actually do it.

I don't find for_each useful. But I do find most other algorithms in
<algorithm> useful, even simple ones like copy.

for_each doesn't save the coder any work because it is just as hard or
harder to put the body of the loop into a functor as it is to just write
the loop. If you already have such a functor laying around for other
purposes, for_each can be great. But creating a functor just so you can
use for_each will often waste more time than it saves.

That being said, I'd love to see these cousins of for_each:

// Call f(first, first+k) for each permutation of
// [first, last) taken k items at a time.
template<class BidirectionalIterator, class Function, class Size>
Function
for_each_permutation(BidirectionalIterator first,
BidirectionalIterator last,
Size k, Function f);

// Call f(first, first+k) for each reversible permutation of
// [first, last) taken k items at a time.
// "reversible" may be the wrong word here. The
// intent is that the reverse of a permutation doesn't
// count as a permutation, and is thus not applied.
template<class BidirectionalIterator, class Function, class Size>
Function
for_each_reversible_permutation(BidirectionalIterator first,
BidirectionalIterator last,
Size k, Function f);

// Call f(first, first+k) for each circular permutation of
// [first, last) taken k items at a time.
template<class BidirectionalIterator, class Function, class Size>
Function
for_each_circular_permutation(BidirectionalIterator first,
BidirectionalIterator last,
Size k, Function f);

// Call f(first, first+k) for each reversible circular permutation of
// [first, last) taken k items at a time.
template<class BidirectionalIterator, class Function, class Size>
Function
for_each_reversible_circular_permutation(BidirectionalIterator first,
BidirectionalIterator last,
Size k, Function f);

// Call f(first, first+k) for each combination of
// [first, last) taken k items at a time.
template<class BidirectionalIterator, class Function, class Size>
Function
for_each_combination(BidirectionalIterator first,
BidirectionalIterator last,
Size k, Function f);

The main difference between these, and std::for_each, is that these do
useful, non-trivial work for you, making the effort to create the
functor worth it.

-Howard

Sune

unread,
Jul 27, 2005, 11:39:16 AM7/27/05
to
Hi Scott!

I have 8 years C++ experience from telecom realtime systems on open
platforms
(UNIX). I have the following to say:

STL is not an abstraction, it's an abomination. STL is a liability in
any software
development project.
- Being completely non-intuitive, it constantly leads even seasoned and
clever
programmers into serious pitfalls and bad design decisions.
- Designers have to rely on 3PP implementations trying to be to clever
when using
memory etc. I've used SUN's and it sucks when things like
deque< deque< vector < string > > > is to be used.
- An abstraction that creates more questions than it's supposed to
answer is
nothing but a joke.

If you don't take my word for it I think the following is proof enough:
- I search on C++ and STL in Amazon and find over 50 books with STL
tips and trix
as their only angle!!!
- I search for STL in ONE periodical ( C/C++ Users Journal ) and I find
over
400 articles!!!

When did STL suckage become taboo?
Can't anyone tell Bjarne to go back to the drawing board on this one?

BRs
/Sune

chris jefferson

unread,
Jul 27, 2005, 11:38:30 AM7/27/05
to
Scott Meyers wrote:
>>From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.
> The other is where people can find open-source examples of real systems
> that actually do it.
>
I've worked on some reasonable-sized C++ projects. When I want something
which an STL function is actually writen for (sort, min, max, etc), then
I'll use it. The main problem with most other tiny loops is even if they
would map nicely to (say) for_each, they require writing a functor,
which has to be declared out-of-line, so unless it's going to be used
alot, its easier to just write the loop out (and will be even easier
once we get "auto").

I mean, considering the body of for_each is basically:
for ( ; __first != __last; ++__first)
__f(*__first);

If writing f is going to take any longer than one line, then it's really
not worth using it from a code-length point of view :)

In my own code I've used boost::lambda to great effect, but I find
myself unwilling to try to introduce it into other people's large
projects as it consists of a large amount of very complex C++ code I
don't understand very well (not that I'm insulting it of course, but I
like to be sure I understand code I'm putting into projects..)

Chris

Dan McLeran

unread,
Jul 27, 2005, 11:38:52 AM7/27/05
to
The project(s) I generally work on are embedded test and measurement
equipment. IMO, STL containers, algorithms, generic programming in
general, Boost, etc. has saved me tons of time. I use std::for_each so
much it looks kinda funny in some places in the code. I believe that a
mastery of these techniques will make you much more productive in the
long run. I work with some people who cling to the 'old ways' like
their lives depend on it. I find that most of the other programmers in
my group make more limited use of these techiniques because their
understanding of them is more limited. One or two guys want to use the
STL more often but can't get their brains around it sometimes. A couple
of the guys don't want anything to do with it and hence will never
understand how to use the STL, by choice. Myself, I'm more of an STL
and generic programming zealot. The project(s) I work on are therefore
overflowing with Boost, Loki, STL, etc. The other guys in my group make
a more limited contribution to the code because they're much slower at
producing it than myself. I believe that their lack of STL
understanding and practice is one of the reasons.

Mirek Fidler

unread,
Jul 27, 2005, 11:40:34 AM7/27/05
to
Francis Glassborow wrote:
> In article <3kovmgF...@individual.net>, Mirek Fidler <c...@volny.cz>
> writes
>
>>I believe that all that "for_each" madness was cause by two factors:
>>
>
>
> Was that really what the quoted person was talking about? I got the
> impression that what he meant was adding algorithms rather than using
> for_each.
>

Well, reading the other responses perhaps I got it wrong.

Anyway, term "manual loop" feels like it is about trivial loops
implemented via STL using functors, adaptors, lambdas and hell knows what.

It certainly is not about complex and hard to implement algorithms like
std::sort.

Mirek

Martin Bonner

unread,
Jul 27, 2005, 12:38:50 PM7/27/05
to
Scott Meyers wrote:
> I got the following email query from a reader. It boils down to asking
> whether it's really reasonable to expect C++ programmers to use STL
> algorithms instead of writing their own loops, a topic I address in my
> "Effective STL," where I argue that it often is. But the reader puts the
> question so much better. It's long, but very much worth reading, IMO:

This is a religious issue.

My particular take on it is:
- if a suitable algorithm exists, use it.
- If the same thing needs to be done in several places to different
container objects, then it may be worth writing an algorithm for it.
- But in general, a straightforward loop is easier to read.

Hmm. I was about to quote the thread "STL functor problem" as an
example, but actually that is quite difficult to handle with a simple
loop (because the simple loop version involves altering the container
while looping).

Nonetheless, I don't find boost::lambda easy to read (the notation for
using member functions is painful), and bind1st / bind2nd / compose,
because very difficult, very quickly.

Most of my code uses loops.

Palik Imre

unread,
Jul 27, 2005, 12:39:54 PM7/27/05
to
Scott Meyers <Use...@aristeia.com> writes:
>
> >From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.

I guess it depends.

Lately I have to refactor more spaghetti then I'd like to, and
replacing the loops with algorithms gives me a pretty good place to
start cutting a few hudred lines long method into pieces. This kind of
algorithmisation also seems to have benefical effect on the data
structures of the module. Because of this very effect of refactoring
data modells, I also find it good to reason about the design in terms
of algorithms.

But I don't think I'd replace a loop in a properly designed and
factored code just because using algorithms is cool. (I have more
than enough problem with the badly designed and factored ones ;-) )

ImRe

Dietmar Kuehl

unread,
Jul 27, 2005, 12:38:17 PM7/27/05
to
Scott Meyers wrote:
[loads of reasonable background snipped]

>>From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.
> The other is where people can find open-source examples of real systems
> that actually do it.

I don't know of open-source examples of real systems using algorithms
a lot and, honestly, I doubt that you will be able to find many of
them. However, still being a strong advocate of the use of algorithms
I will address the first question.

The short version of my answer is this: Generic Programming is the way
to go for all algorithmic needs but we don't yet have the necessary
tools in place for their general practical use.

There are many problems with the current set of algorithms in the
Standard C++ Library which hamper their use in everyday work. Here
are the problems and the cure I envision:

- STL addresses only a very specific albeit basic set of algorithms.
If you need something different than an algorithm on a sequence you
will not get any help from the standard C++ library. This is a well
recognized problem which is addressed at least partially by people
identifying the basic concepts for other problem domains and creating
corresponding algorithms. The Boost Graph Library is an example of
addressing a different problem domain than sequences. Since the
algorithmic structures differ widely, it is only occasionally viable
to build upon algorithms from another problem domain. For the rest of
this discussion I will assume that we are only concerned with sequence
algorithms.

- The algorithms library is quite incomplete although most of the basic
algorithms are all there. It is just a lack of some need combinations
or a lack of flexibility. For example, to use 'mismatch()' you need
to know which of the two sequences is shorter and pass it in as first
sequence. There is no overload taking two pairs of iterators or two
ranges which makes it effectively impossible to use 'mismatch()' on
two input sequences which are quite frequent in my code because it is
often trivial to create an input iterator but not that easy to create
even a forward iterator. Another example is the lack of something
like 'copy_until()'. Of course, a 'find_if()' followed by a 'copy()'
on the subsequence does the trick - except that it again does not
cover the input iterator case.

- For many typical cases the algorithms are relatively hard to use due
to their need for pairs of iterators. Although the flexibility to
operate on subsequences is essential for a fundamental layer, it
makes their quite hard and unnecessarily wordy. It should be
possible to pass a "range" which exposes a 'begin()' and an 'end()'
(i.e. containers happen to be range objects) in addition to overloads
taking sequences. Although these algorithms would mere extract the
sequence and forward it to a corresponding algorithm, this has a huge
impact on usability and also allows algorithm chaining, i.e. passing
the result of one algorithm immediately as the input of another
algorithm.

- The STL algorithms erroneously operate on a concept combining access
and traversal. There are loads of problems resulting from this and
I have written about many of them in the past (search for "property
map" and/or "data accessor" in comp.lang.c++*). The cure is for the
fundamental algorithms to operate on cursors (iterator like entities
which are used for traversal and which expose a "key" for the current
position) and property maps (function like entities which take a "key"
and access an object associated with this key; note, that this
association is typically just a pointer dereference or an index
access in a array). This separation might seem contradictory to the
usability issue mentioned previously but it actually isn't: the
algorithms can easily have simple wrappers operating on iterators and
conjuring up a default property map before delegating to the more
general algorithm. However, the needed flexibility is still there and
can be used where needed which is more frequently the case than
possibly expected.

- Generic algorithms are usually configured using some form of functor.
However, given the current standard library it is pretty hard to just
create simple functors on the fly without creating a separate function
which kind of defeats the use of algorithms in the first place. The TR1
provides improved functors which cure some of biggest problems.
Something like Boost::Lambda goes a huge step further and allows easy
creation of many functors. The real cure cannot, however, be done in a
pure library approach and what is effectively needed are true "Lambda
Expressions" whatever this really is in detail.

These are the most problematic areas I have found with STL in the past
including outlines of how to address these. I'm pretty sure that there
are still problems lurking once a cure for each of them is in place
but I would expect that addressing the above problems will take use much
further. The current STL was installed without any real experience and
actual use typically reveals real problems. I'm still convinced that
Generic Programming is the correct approach, although the current
interface to algorithms leaves much to be desired.

In my own code, I'm currently using a relatively pragmatic approach to
the use of algorithms: if there is something which fits easily or with
only a moderate amount of tweaking, I use it in favor of a hand-crafted
loop. Otherwise I just write the loop. After all, the intention is to
make live easier. However, I always try to figure out whether the
reason for writing the loop would be removed by the approaches outlined
above in an attempt to reveal additional problems.
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

ThosRTanner

unread,
Jul 27, 2005, 12:40:39 PM7/27/05
to

Scott Meyers wrote:
<snip>


> From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.
> The other is where people can find open-source examples of real systems
> that actually do it.
>
> The first question, especially, should really be answered only by people
> who spend a lot of time working on real C++ systems. I welcome comments on
> both questions, as I hope to be able to enlighten not only my reader, but
> also myself.

I've had a look at the system I work on and the number of for_each is
pretty minimal. It has generally been used where the operation has
pretty limited side effects - either modifying the object it is passed,
or modifying some global data (such as writing to cout), and where any
pre/post processing doesn't have any bearing on what happens in the
loop (or what has happened in the loop).

As soon as you try to do anything more complex than that, things get
pretty hairy indeed. For an algorithm that works:

begin
do a lot of initialisation with variables
for each member of the set
{
modify a few of the variables
maybe modify the member
}
fiddle about a bit more with the variables
return some result
end

you have to create a class that:
1) Has all the variables you need defined as members
2) Initialises them all in the constructor
3) provide an operator()(member type&) which does the loop code
4) provide a final operation to do the post processing and return the
result value

This isn't impossible, but it is surprisingly painful. It's probably
something to do with the operator()(thing &) syntax, which feels
unnatural.

And none of it works if you have a loop that does
for (thing = ctr.begin(), i = 0; thing != ctr.end(); ++thing, ++i);

Marco Manfredini

unread,
Jul 27, 2005, 1:30:30 PM7/27/05
to
Scott Meyers wrote:
> [...] The other is where people can find open-source examples of real

> systems that actually do it.

For that question: As a nonrepresentive estimate, I've put together a
script that scans all the c++ source files (ie: C|cpp|cc|H|cpp|hh) from
the slackware 10.1 source cd's for the pattern: (find_if|remove_if
for_each|bind1st|bind2nd).

Here are the packages that contained any of these:
blackbox-0.65.0 gcc-3.3.4 oprofile-0.8.1 kdeedu-3.3.2 kdegraphics-3.3.2
kdemultimedia-3.3.2 kdenetwork-3.3.2 kdepim-3.3.2 kdeutils-3.3.2
fluxbox-0.1.14 wv2-0.2.2 lftp-3.0.13 abiword-2.0.12 fluxbox-0.9.12
ImageMagick-6.1.9-0

Number of lines with:
find_if 37
remove_if 10
for_each 104
bind1st 5
bind2nd 22
and this includes libstdc++ and both versions of fluxbox..

The total number of c++ source lines scanned was: 8811927

i.e. in ~8.8 Milliones lines of open source c++ code, less than 200 uses
of comparably important constructs from <algorithm> and <functional>.
Not sure if that means something...

Marco

Mirek Fidler

unread,
Jul 27, 2005, 1:36:26 PM7/27/05
to
> When did STL suckage become taboo?

Hehe, perhaps we should form "STL haters" club...

Anyway, there is one good thing that STL did for me:

It helped to finaly refine templates in core language and compilers.

> Can't anyone tell Bjarne to go back to the drawing board on this one?

Well, does it really matter?

As long as C++ core language does not screw my libraries, it is OK with me.

Mirek

P.S.: I hope that it is not quite off-topic: as an alternative, I dared
to develop, maintain and use this:

http://upp.sourceforge.net/srcdoc$NTL$en-us.html

the main difference is the lack of "copy-constructible" and "assignable"
requirements, I believe that it immediately cuts off at least 70% of
problems that I have encountered in STL.

arke...@myrealbox.com

unread,
Jul 27, 2005, 1:32:52 PM7/27/05
to
Quite simply, I am learning to use standard algorithms as a replacement
for loops. I can see, using a simple text search, how slowly more and
more of manual loops are replaced with for_each, transform, etc,
however I only invest a few minutes in trying to 'reshape' a manual
loop to fit an algorithm, time is money.

In general my programming style has moved towards deep callstacks with
specialized inline functions, which are amenable to binding, being the
norm. I do not know if this is good or not, but It seems to be my
'natural' progression in the face of the STL/boost libraries. IMHO it
is FAR more readable to use a(well spaced) for_each than most
equivalent loop constructs, but I think that this is a matter or style
and experience.

That being said, I have the luxery of working with modern tools, on
fresh 'modern C++' source code, so I doubt this is very representative.

JJJ

M Jared Finder

unread,
Jul 27, 2005, 1:33:13 PM7/27/05
to
Russell Hind wrote:

> Sorting them by description gives us
>
> std::sort(Excipients.begin(), Excipients.end(),
> bind(less<string>(), bind(&Excipient_c::GetDescription, _1),
> bind(&Excipient_c::GetDescription, _2)));
>
> Eric Niebler's BOOST_FOREACH macro has been accepted and will hopefully
> appear in boost-1.34 which may well simplify things as with that, the
> above could be
>
> shared_ptr<Excipient_c> Excipient;
> BOOST_FOREACH(Excipient, Excipients)
> {
> if (Excipient->GetDescription() == Name)
> {
> break;
> }
> }
>
> Which is much more readable IMHO even if it is using a macro. The
> disadvantage of BOOST_FOREACH is that I don't think you can get access
> to the iterator if Excipient which the std::algorithms and hand-coded
> loops give you.

Just to be pedantic, the second example is the equivalent of
std::find_if, not of std::sort. std::sort can not be written
efficiently using BOOST_FOREACH, as it requires mutating the underlying
sequence in multiple passes.

-- MJF

Rodrigo Strauss

unread,
Jul 27, 2005, 2:40:17 PM7/27/05
to
I'm working in the financial area, programming (socket) server software
to various stock exchange terminals. I use STL extensively, and it
saves me large amounts of time. With the .NET marketing avalanche, I
took some time do think how I would reengineer the software in C#. My
conclusion was that I would be less productive with the
"high-productivity languages" (C#, Java) than I am using C++ and STL.

I see no reason to use loops when you have a STL algorithm, everyone
can figure out what std::for_each does, even a non C++ programmer. Why
do I need to repeat the same for(xx::interator i = x.begin... over and
over again? It's error prone, you have the temptation of using
copy/past all over the code, and we all know where it ends. You can use
an iterator from an instance of container as the end test of another
instance, just to mention one common mistake.

I use maps, vectors and STL algorithms to control and filter connected
users and the assets users want to view/sign. The STL code is so
trivial, so verbose, that I use it to show people that C++ with STL is
not as hard as most people think.

Example:

for_each(clients.begin(), clients.end(), CancelAllSignatures());

I think it's much better than a loop. It's obvious we will cancel all
signatures from all clients. But it's good because the code reader
doesn't need to know the implementation detail. And every good
IDE/Editor has a way to send you to the definition of
ClearAllSignatures just pressing a key.

And I sure agree we need better error messages from the compiler when
using templates.

Regards,

Rodrigo Strauss
http://www.1bit.com.br

Keith H Duggar

unread,
Jul 27, 2005, 2:39:30 PM7/27/05
to
chris jefferson wrote:
> In my own code I've used boost::lambda to great effect,
> but I find myself unwilling to try to introduce it into
> other people's large projects as it consists of a large
> amount of very complex C++ code I don't understand very
> well (not that I'm insulting it of course, but I like to
> be sure I understand code I'm putting into projects..)

Yes, I've run into the same pitfall. I used boost's shared
pointer, and regex library in one of my projects. When I
passed the code onto a colleague, he had a hell of a time
getting boost setup on his system at work (permission
problems, build problems, etc). Eventually he became so
frustrated he just (sadly) recoded the regex portions of
the code in Java (which has a regex library) and then just
linked to the remaining C++.

And as to understanding boost code, I realize I'm not so
bright but understanding some of the boost code can be a
real mind frak [ref Battlestar Galactica].

Howard Hinnant

unread,
Jul 27, 2005, 2:41:00 PM7/27/05
to
In article <3kprvgF...@individual.net>, Mirek Fidler <c...@volny.cz>
wrote:

> P.S.: I hope that it is not quite off-topic: as an alternative, I dared
> to develop, maintain and use this:
>
> http://upp.sourceforge.net/srcdoc$NTL$en-us.html
>
> the main difference is the lack of "copy-constructible" and "assignable"
> requirements, I believe that it immediately cuts off at least 70% of
> problems that I have encountered in STL.

Changes to list, set, multiset, map, multimap are already in the C++0X
working paper which greatly reduce the assignable requirement:

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276

I hope to further reduce the need for both CopyConstructible and
CopyAssignable both in containers, and in algorithms, with the
introduction of the rvalue reference:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html

-Howard

Carlos Moreno

unread,
Jul 27, 2005, 7:07:43 PM7/27/05
to
Scott Meyers wrote:

>>From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.

> The other is where people can find open-source examples of real systems
> that actually do it.
>

> The first question, especially, should really be answered only by people
> who spend a lot of time working on real C++ systems. I welcome comments on
> both questions, as I hope to be able to enlighten not only my reader, but
> also myself.

As a huge advocate of the use of STL algorithms over hand-coded loops,
but being also pragmatic when it comes to write my code, I have to add a
few things to what has been said:

1) A lot of people seem to be forgetting that using STL algorithms is
a form of "refactoring code". People keep insisting that a hand-coded
loop is easier to read (presumably because you're seeing exactly what
the loop is doing).

However, I must disagree with such a statement in the general case;
In a simple, isolated short fragment of code, maybe a hand-coded loop
showing exactly what is going on may be easier to read. But when a
function that could be 30 lines long with just a few if-elses and 5
calls to STL algorithms becomes 200 lines, then it is not easier to
read.

A *named* call to an algorithm reveals exactly and *up front* what
the code is going to do -- and it's easier to skip if you're, say,
searching for a bug that you happen to know is further down the code.

A hand-coded loop takes a few seconds (or a few minutes) inspecting
it to *realize* what the code is doing.

All this, in addition to the fact that in one line of code there are
less chances to introduce a bug due to a typo or an oversight (aaahh,
how many times have I hated myself for being lazy and coding a loop and
then hours and hours of debugging later realizing that I made a typo
and, say, put the wrong name to the loop-control variable (and by
chance I got the name of another one that had been declared, and thus
the compiler didn't flag an error)

IMHO, the value of having *one* line of code for *one action* is very
high and compensates for many of the inconveniences.


2) True that item (1) above is partly contradicted if we shift the
focus of your original question from "STL algorithms vs. hand-coded
loops" to "STL algorithms vs. a hand-coded *function*".

For instance, it is really easier to argue that the second line is
better/easier-to-read than the second one:

pos = count_if (cont.begin(), cont.end(), bind2nd(greater<int>(0)));

pos = num_positive_elements (cont);

(of course, in the implementation of the function num_positive_elements,
I would make it a one-liner, using the first line above :-)).

Actually, there would be an advantage in using such wrapper, even if
one is going to code with count_if and bind2nd: you get rid of the
hardcoded <int> in the code:

template <typename Container>
int num_positive_elements (const Container & c)
{
typedef typename Container::value_type T;
return count_if ( ...., bind2nd(greater<T>(0)));
}

Coming back to the two one-liner alternatives to count positive values,
the STL alternative in that case has the advantage that you don't have
to write any additional code -- you're refactoring with functions that
are already there.


3) You said it beautifully in your Effective STL book, item 47: Avoid
producing write-only code. In fact, a lot less than the examples you
show, I would still call "write-only" :-)

Let's say that my rule of thumb goes more or less like: "Using an STL
algorithm is better until proven the opposite". That is, when making
a decision, I try to maintain a strong bias towards using STL algorithms
and only resort to a hand-coded loop when I'm absolutely convinced that
using an STL algorithm is (a) completely twisted and inconvenient, or
(b) beyond my level of knowledge/skills with the STL.

Obviously, the above depends heavily on the level of familiarity with
the STL of each programmer. While implementing copy_if in terms of
remove_copy_if may be a trivial "warming-up exercise" for you (or for
John Potter, who, as I recall, came up with a *correct* implementation
a while ago in this newsgroup), it is rather twisted for me (and I guess
for many other programmers). Yes, my solution has been to implement
my own version of copy_if with a hand-coded loop in it :-)


Perhaps it would be a good idea that people post *concrete examples*
of a given situation, and their choice for that particular situation;
for instance, if I have a list of Packets to be transmitted, and each
Packet has a time tag (the time at which it was dispatched to be sent),
and I'm looking for the oldest, then I'd definitely use the STL:

class cmp_timetag
{
public:
bool operator() (const Packet & p1, const Packet & p2) const
{
return p1.sent_at() < p2.sent_at();
}
};

min_element (transmit_queue.begin(),
transmit_queue.end(),
cmp_timetag());


If I need to count how many strings are longer than a given string,
I'd definitely use the STL:

class longer_than
{
const string & s;
public:
longer_than (const string & s) : s(s) {}

bool operator() (const string & item) const
{
return item.length() > s.length();
}
};

count_if (names.begin(), names.end(), longer_than("Carlos"));

Yes, writing the functor implies an amount of work up front, but the
readability of the resulting code is, IMHO, greatly improved (plus,
I'll bet you -- 1 out of 10 times, an average programmer *will*
forget to initialize the counter to 0 in the hand-coded loop
version of the above! :-)).


HTH,

Carlos
--

Dave Harris

unread,
Jul 27, 2005, 7:05:42 PM7/27/05
to
Use...@aristeia.com (Scott Meyers) wrote (abridged):

> whether it is practical to replace many manual loops with algorithm
> calls.

Only a small fraction of them. For a given loop, it depends on the
complexity of the algorithm and whether I need to write a predicate.

I use even simple algorithms if I don't need to write a predicate for
them, eg rotate() and some uses of find(). I don't use for_each() because
that usually does need a predicate, and I won't write a predicate in order
to use find(). Some algorithms, such as the sorting and searching ones,
are worth writing a predicate in order to use.


> The first question, especially, should really be answered only by people
> who spend a lot of time working on real C++ systems.

That has been my job for over 10 years.

-- Dave Harris, Nottingham, UK.

Dave Harris

unread,
Jul 27, 2005, 7:06:49 PM7/27/05
to
rodrigo...@gmail.com (Rodrigo Strauss) wrote (abridged):

> I see no reason to use loops when you have a STL algorithm, everyone
> can figure out what std::for_each does, even a non C++ programmer. Why
> do I need to repeat the same for(xx::interator i = x.begin... over and
> over again? It's error prone, you have the temptation of using
> copy/past all over the code, and we all know where it ends. You can use
> an iterator from an instance of container as the end test of another
> instance, just to mention one common mistake.

But that mistake is also possible with the standard algorithms. It's one
of their design flaws.

for_each( v1.begin(), v2.end(), functor() );

-- Dave Harris, Nottingham, UK.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Branimir Maksimovic

unread,
Jul 27, 2005, 7:15:14 PM7/27/05
to
Scott Meyers wrote:

> >From my perspective, there are two questions here. One is, as I said,

> whether it is practical to replace many manual loops with algorithm calls.

Algorithm functions are very specific in a way that they work with
pointers that represent sequences.
Most loops are not of that kind, rather they use counters and/or
indexing variables.
Simple loop like:

int a[10];
for(size_t i = 0; i<10;++i)a[i]=i;

can be efficiently translated to algorithm function.

struct Nplus{
Nplus(int n=0):n_(n){}
int operator()(){ return n_++; }
int n_;
};

generate(a,a+10,Nplus());

but, people are not used to think that way and with more complex loops
there is really a question if such translation would be pragmatic.
Regarding simulations of lambda functions and bind, I never do this,
rather, if necessary, provide specific overloads.

> The other is where people can find open-source examples of real systems
> that actually do it.

Generally people try to avoid templates, but that was some time ago.
Now, since compilers are improving I don't see reason to avoid
them in the future.

Greetings, Bane.

Andrew Ward

unread,
Jul 27, 2005, 7:19:25 PM7/27/05
to
>>From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.


I work on a ~50K line program in the electronics/FPGA field which uses
the Qt library. Initially when I started working on the project I tried
to use algorithms with function objects, but eventually moved away from
them. I got sick of passing class member variables into the function
object constructors, it just seemed uneccessary when you can just write
the loop in the function directly. With Qt4.0 they have a foreach macro
which I will start using when I port from Qt 3.3 to Qt 4. Essentially
our application seems to be moving further and further away from the
standard library, we don't even use std::string because QString makes
i18n so easy. I think I first started trying to use the standard
algorithms because I thought they would be a cool challenge, but the
novelty soon wore off. I do think they are seriously too different from
other major programming languages like C# and Java and this is important
when hiring people, especially in my area of New Zealand, where the
local university typically only teaches Java, C and a little Haskell
through the course of a computer science degree.

Peter Dimov

unread,
Jul 27, 2005, 7:14:08 PM7/27/05
to
Russell Hind wrote:

> I took up to a year for me to be confident enough with bind to decide it
> may be better than a hand-written loop. But there are cases where it
> just looks complicated. e.g. some of our data structures have a
> 'get_name' member function with returns a string. We want to find which
> one has a particular name and the bind expression comes out to be:
>
> find_if(m_ExistingExcipients.begin(), m_ExistingExcipients.end(),
> bind(equal_to<string>(), bind(&Excipient_c::GetDescription,
> _1), Name));

[...]

> Sorting them by description gives us
>
> std::sort(Excipients.begin(), Excipients.end(),
> bind(less<string>(), bind(&Excipient_c::GetDescription, _1),
> bind(&Excipient_c::GetDescription, _2)));

The current CVS version of boost::bind (soon to be released) supports

bind(&Excipient_c::GetDescription, _1) == Name

and

bind(&Excipient_c::GetDescription, _1) <
bind(&Excipient_c::GetDescription, _2)

Keith H Duggar

unread,
Jul 27, 2005, 7:14:52 PM7/27/05
to
Marco Manfredini wrote:
> i.e. in ~8.8 Milliones lines of open source c++ code, less
> than 200 uses of comparably important constructs from
> <algorithm> and <functional>. Not sure if that means
> something...

Yes. It indicates that the comments describing said uses of
find_if, bind1st, etc are typically about 40,000 lines long.

:-)

Mirek Fidler

unread,
Jul 27, 2005, 7:13:26 PM7/27/05
to
> Changes to list, set, multiset, map, multimap are already in the C++0X
> working paper which greatly reduce the assignable requirement:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276

Thank you Howard, but that will not make it. What I really need are
random access containers with neither assignable nor copy-constructible
requirements. Those are kinds of things needed in GUI development.

This proposal speaks only about assignable and only in context of
node-based containers. That is useless, at least for me.

> I hope to further reduce the need for both CopyConstructible and
> CopyAssignable both in containers, and in algorithms, with the
> introduction of the rvalue reference:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html

Sure, I know, perhaps you remember that I am big fan.

BTW, have you finally realised how important are composition rules there?

Well, even without them this will help a lot, but with them C++ would be
paradise.

Mirek

P.S.: If I remember well, last time we were discussing, you was
suggesting that with (implicit) destructive copy it is impossible to
implement merge sort. Well, I was not sure at the moment, but now I know
that it is possible and trivial... In general, default destructive copy
has only a little impact on algorithms library.

Mirek Fidler

unread,
Jul 27, 2005, 7:12:27 PM7/27/05
to
> do I need to repeat the same for(xx::interator i = x.begin... over and

It is problem of iterator syntax, not of 'for'.

> Example:
>
> for_each(clients.begin(), clients.end(), CancelAllSignatures());
>
> I think it's much better than a loop. It's obvious we will cancel all
> signatures from all clients.

Is it? What CancelAllSignatures returns? Is it a functor?

> But it's good because the code reader
> doesn't need to know the implementation detail.

Impementation detail of what? for_each? Come on.

Mirek

White Wolf

unread,
Jul 27, 2005, 7:19:47 PM7/27/05
to
Mirek Fidler wrote:
>>> From my perspective, there are two questions here. One is, as I said,
>> whether it is practical to replace many manual loops with algorithm
>> calls.
>
> IME, no.
>
> IME, and I know most people will disagree with me, the most effective
> solution is to avoid node base container and iterators and use indicies
> everywhere.

Good luck for having an indexed map. :-)

> Makes coding trivial, short and unlikely to be prone to hard to catch
> errors like silently invalidated iterators or iterators going out of
> range (ok, I know that some STL implementation provide runtime checks,
> but thing is that these kinds of error are much less likely to happen
> with indicies).

In what way are indices more likely to stay valid than iterators? I mean if
I have index #10 tored away for a vector, and then I remove stuff and have
only 5 elements, my index becomes rogue just the same way an iterator would.
I just do not see (or believe) that it is any less likely with indices than
iterators.

> I believe that all that "for_each" madness was cause by two factors:
>

> - it looks "cool" and "advanced"

Well, here we disagree again. I think that Claudia Schiffer looks cool, and
tablet PCs look advanced. :-)))

Of course, it *is* motivation for some people to "just do it" and make code
just to show off. But IMHO most successful C++ programmers (still being
programmers and alive ones ;-) ) do not do that. One uses a language (or
library) feature, because the design is expressed best with that solution.

> - it is annoying to create for loop for STL containers as iterators
> syntax is usually way too ugly
>
> for(std::vector<std::string>::iterator it = c.begin(); it != c.end();
> it++)

Indeed it is! It is so annoying, that I had to work through already 3 nites
in this (well, the previous) month to fix code like the above. Why? In
that code there is one error, one serious design flow and one possible
performance hit! In a single line of code!

What does that tell us? It is *indeed* advicable to use STL algorithms,
instead of badly written loops.

1.) ++it and *not* it++. For gods sake: Mean what you say, and say what you
mean!

2.) std::vector<std::string>::iterator is something, for which I flunk
pupils. Over 40 ones. Is it really necessary to repeate that
std::vector<std::string> in 254 places of the code? How about having a

typedef std::vector<std::string> str_table_t;

inside that class having that very container as a member? So if tomorrow I
decide to use list, or deque, or my own home grown STL-compatible
fixed-capacity-array class all I need to do is to change that one line of
code. (As long as the new container fits the bill, but it does, that is why
I chose it.)

3.) Unless the container itself is being changed inside the loop, the end()
iterator is better be saved away into a const variable before the loop.

4.) NITPICKING. Use variable names which tell what the thing *represents*,
not how it is implemented. c might be A OK for the speed of light, maybe
for a character (but conventionally that is ch), but it is a very bad name
for a vector of strings. Since that is a "symbol table", or a "name list",
or a "state list" or... Anything, but a container and a c. At least in
application code, which most of us is supposed to write.

> I guess this is something that nobody really likes... (I think that at
> the moment "auto" extension is introduced, all that "for_each" stuff
> will be silently forgotten in year or two).

I strongly doubt that. I think that as soon as lamba expressions (and
possibly closure like things) get supported by the core language better, we
will see a lot more use of algorithms than we do today.

Why do I say that algorithms are better than hand written loops? Reason #1
is the 3 mistakes made in that loop above. Reason #2 is that I am
*extremely* lazy. I am ready to waste my time with internet chats, with
many silly things... But I do not like spending even 2 seconds (while I am
in a thinking process in understanding code) to find out what that loop
stands for. std::for_each already gives a hint on that-

> As for me, I prefer easy to maintain variant
>
> for(int i = 0; i < c.GetCount(); i++)

God save me! ++i. Say what you mean, mean what you say! Pretty please!
:-)

GetCount()? I like my Brasilian coffee, grown under a different Sun. Why
on Earth GetCount()? How does that name is better than size()? Not
counting the absolutely unneded 3 letters (get), the unconventional casing
(most people are used to see Types started with uppercase letters but not
functions) and the fact that it suggests that it *counts* something, while
it does not. :-(((

I must be very slow now, it is late here. But I have failed to grasp how
two, badly written, for loops are proving that algorithms are bad.

--
WW aka Attila
:::
Historically speaking, the presence of wheels in Unix has never precluded
their reinvention. - Larry Wall

Bob Bell

unread,
Jul 27, 2005, 7:54:52 PM7/27/05
to
Mirek Fidler wrote:
> > Changes to list, set, multiset, map, multimap are already in the C++0X
> > working paper which greatly reduce the assignable requirement:
> >
> > http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
>
> Thank you Howard, but that will not make it. What I really need are
> random access containers with neither assignable nor copy-constructible
> requirements. Those are kinds of things needed in GUI development.

I'm curious; what kind of non-assignable, non-copy-constructible
objects do you need to put into containers in GUI development? Or did I
misunderstand something?

Bob

Hendrik Schober

unread,
Jul 28, 2005, 7:16:58 AM7/28/05
to
w...@seed.net.tw <w...@seed.net.tw> wrote:
> [...]
> Let's say, If I already know how to program in C++, why
> bother spending longer time to learn STL [...]

Er...because you do /not/ know how to program
in C++ unless you learned how to use the STL?

> [...]


Schobi

--
Spam...@gmx.de is never read
I'm Schobi at suespammers dot org

"Coming back to where you started is not the same as never leaving"
Terry Pratchett

Mirek Fidler

unread,
Jul 28, 2005, 7:17:29 AM7/28/05
to
> function that could be 30 lines long with just a few if-elses and 5
> calls to STL algorithms becomes 200 lines, then it is not easier to
> read.

If only that was true. I guess that very often, almost in any
non-trivial case, hand coded loops are as long or even shorter than
algorithms. Which in the end is what was pointed out in original post.

> Perhaps it would be a good idea that people post *concrete examples*
> of a given situation, and their choice for that particular situation;
> for instance, if I have a list of Packets to be transmitted, and each
> Packet has a time tag (the time at which it was dispatched to be sent),
> and I'm looking for the oldest, then I'd definitely use the STL:
>
> class cmp_timetag
> {
> public:
> bool operator() (const Packet & p1, const Packet & p2) const
> {
> return p1.sent_at() < p2.sent_at();
> }
> };
>
> min_element (transmit_queue.begin(),
> transmit_queue.end(),
> cmp_timetag());
>
>
> If I need to count how many strings are longer than a given string,
> I'd definitely use the STL:
>
> class longer_than
> {
> const string & s;
> public:
> longer_than (const string & s) : s(s) {}
>
> bool operator() (const string & item) const
> {
> return item.length() > s.length();
> }
> };
>
> count_if (names.begin(), names.end(), longer_than("Carlos"));

Well, I guess you have just proved that my point above is correct...

Hand coded, you would avoid using two predicate classes and reduced
above code to much less lines.

(Also, a little bit off topic, I would store length of s rather than the
reference to s in longer_than, but that is just me :)

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 7:22:32 AM7/28/05
to
Bob Bell wrote:
> Mirek Fidler wrote:
>
>>>Changes to list, set, multiset, map, multimap are already in the C++0X
>>>working paper which greatly reduce the assignable requirement:
>>>
>>>http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
>>
>>Thank you Howard, but that will not make it. What I really need are
>>random access containers with neither assignable nor copy-constructible
>>requirements. Those are kinds of things needed in GUI development.
>
>
> I'm curious; what kind of non-assignable, non-copy-constructible
> objects do you need to put into containers in GUI development? Or did I
> misunderstand something?

Well, namely, widgets.

Like Array<Button>.

In general development, things like file streams are sometimes needed as
well.

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 7:19:23 AM7/28/05
to
>>IME, and I know most people will disagree with me, the most effective
>>solution is to avoid node base container and iterators and use indicies
>>everywhere.
>
>
> Good luck for having an indexed map. :-)

Like this:

http://upp.sourceforge.net/src$ArrayMap$en-us.html

?

:)

(And yes, it is faster than std::map for average case, before you start
asking).

> In what way are indices more likely to stay valid than iterators? I mean if
> I have index #10 tored away for a vector, and then I remove stuff and have
> only 5 elements, my index becomes rogue just the same way an iterator would.
> I just do not see (or believe) that it is any less likely with indices than
> iterators.

Well, loops based on indicies have usually i < c.GetCount() (or i <
size() if you insist) condition. That rules out most of problems.

Usually, the body of loop performs some single action. This action might
modify the container. If this accidentally happens with iterators and
say vector, you are out of luck (iterators get invalidated, or get out
of range and you are testing just equality to End).

With indicies it usually works as expected out of box without trouble.

If you want example, go several news threads back and see "Vector
Iterator headache". And I guess it is at least one post per week that
deals with exactly the same problem.

And, BTW, performance wise, there is no difference either. Compiler will
emit similary effective code regardless you are using indicies or iterators.

> Of course, it *is* motivation for some people to "just do it" and make code
> just to show off. But IMHO most successful C++ programmers (still being
> programmers and alive ones ;-) ) do not do that. One uses a language (or
> library) feature, because the design is expressed best with that solution.

Well, why it is "recommended style" then? That is I think OP's issue.

> 2.) std::vector<std::string>::iterator is something, for which I flunk
> pupils. Over 40 ones. Is it really necessary to repeate that
> std::vector<std::string> in 254 places of the code? How about having a
>
> typedef std::vector<std::string> str_table_t;
>
> inside that class having that very container as a member?


Great. Now you had to introduce another type just to keep your broken
machine going. Adding entities to code just to make your loops run does
nothing than increase your code complexity.

> So if tomorrow I
> decide to use list, or deque

So what? How str_table_t is going to help you? Do you plan to replace
ALL occurences of std::vector<std::string> in your code with list or
deque? Or do you plan to introduce

str_table_for_my_Foo_class_implementation_t

like things?

Anyway, for me this is much more simple. If I want to replace one of my
vector-like container with another, I just do that. They have similar
interface and no additional types needed to write code with them (thanks
to indicies).

Mirek

Gene Bushuyev

unread,
Jul 28, 2005, 7:20:41 AM7/28/05
to
"Scott Meyers" <Use...@aristeia.com> wrote in message
news:MPG.1d50a59dd...@news.hevanet.com...
...

>>From my perspective, there are two questions here. One is, as I said,
> whether it is practical to replace many manual loops with algorithm calls.
> The other is where people can find open-source examples of real systems
> that actually do it.

Can only answer the first question from my personal experience. In our code
base there are plenty of manual loops and many less algorithms. When I tried
using algorithms intensively in one library I found out two problems with
that.

Firstly, algorithms often require some sort of functor object. As a result
implementation was littered with hundreds of small classes. And those
classes may be separated by many lines of code from the place where they are
used, because the standard doesn't allow template instantiation on local
classes.

Secondly, since the only way to pass the local context is to copy variables
(or pointers/references) there is a lot of constructors that are copying
stuff and it's very inflexible and a pain to extend. The real code is often
designed by try and error process. It's not unusual to have several attempts
before settling on a certain algorithm and functor. Imagine how much
unnecessary typing it involves. So you can find that most algorithms that
are frequently used are those that do not require creating a functor. Manual
loops don't have these deficiencies: the code that does something is
localized right at the place of where the action is, and the local context
is easily available. Now, lambda and binders have some of same advantages.
But they also have problems. Compilation errors can be baffling, and it's
practically impossible to debug nontrivial compositions.

- gene

Francis Glassborow

unread,
Jul 28, 2005, 7:19:55 AM7/28/05
to
In article <dc919p$94s$1...@phys-news1.kolumbus.fi>, White Wolf
<wo...@freemail.hu> writes

>In what way are indices more likely to stay valid than iterators? I mean if
>I have index #10 tored away for a vector, and then I remove stuff and have
>only 5 elements, my index becomes rogue just the same way an iterator would.
>I just do not see (or believe) that it is any less likely with indices than
>iterators.

Very simply:

if(index < v.size()) v[index] ...

remains valid. IOWs the precondition for v[n] to be valid if v is a
std::vector<type> is that v currently has at least n elements. There is
an extra precondition for *v_iter to be valid; that the vector has not
been moved since the value in v_iter was obtained. That pre-condition is
not even checkable in the general case.


--
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

Dietmar Kuehl

unread,
Jul 28, 2005, 7:45:13 AM7/28/05
to
chris jefferson wrote:
> I mean, considering the body of for_each is basically:
> for ( ; __first != __last; ++__first)
> __f(*__first);
>

Although I think that 'for_each()' is not really the most intersting
algorithm in STL, it can actually be implemented quite differently
and take advantage of some optimizations, especially if the function
used is pretty simple. For example, 'for_each()' can do loop unrolling
if the sequence provides random access.
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

Dietmar Kuehl

unread,
Jul 28, 2005, 7:45:57 AM7/28/05
to
Mirek Fidler wrote:
> P.S.: I hope that it is not quite off-topic: as an alternative, I dared
> to develop, maintain and use this:
>
> http://upp.sourceforge.net/srcdoc$NTL$en-us.html
>
> the main difference is the lack of "copy-constructible" and "assignable"
> requirements, I believe that it immediately cuts off at least 70% of
> problems that I have encountered in STL.

The really major difference is that you are addressing something
entirely different than STL does! You are not at all addressing
algorithms which is the primary goal of STL. You have some containers
as has STL but the containers for STL, albeit useful, are mostly
there as examples of sequences. I'd bet that your containers could
also be used with STL algorithms. The basic idea of STL is that
algorithms are written once and can then be applied to all data
structures having a reasonable representation (e.g. can be traversed
in some form or have some form of random access, depending on the
algorithm).


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Howard Hinnant

unread,
Jul 28, 2005, 7:49:03 AM7/28/05
to
In article <1122507091....@g14g2000cwa.googlegroups.com>,
"Bob Bell" <bel...@pacbell.net> wrote:

> Mirek Fidler wrote:
> > > Changes to list, set, multiset, map, multimap are already in the C++0X
> > > working paper which greatly reduce the assignable requirement:
> > >
> > > http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
> >
> > Thank you Howard, but that will not make it. What I really need are
> > random access containers with neither assignable nor copy-constructible
> > requirements. Those are kinds of things needed in GUI development.
>
> I'm curious; what kind of non-assignable, non-copy-constructible
> objects do you need to put into containers in GUI development? Or did I
> misunderstand something?

Well, not specific to GUI development, but certainly applicable there,
I'm thinking vector<unique_ptr<T>> might be extremely useful.
unique_ptr is like auto_ptr, but not copyable, only movable. The T
might be a windowing abstract base class that is also not copyable, and
perhaps not even movable.

Because unique_ptr is not copyable, it is safe to use within generic
code such as containers and algorithms. If the generic code attempts a
copy, it will fail at compile time. However because unique_ptr is
movable, and because much generic code only requires moving instead of
copying, significant functionality with existing (but reimplemented)
standard containers and algorithms can be made available.

vector<shared_ptr<T>> is also useful in this context. But
vector<unique_ptr<T>> is significantly faster, having the exact same
overhead as vector<T*>. If you need the semantics of shared ownership,
shared_ptr is the tool you need. However if you need the semantics of
unique (unshared) ownership, then unique_ptr can enforce that
requirement at compile time. And yet you can still do things like:

vector<unique_ptr<int> > v1, v2;
....
remove_copy(make_move_iterator(v1.begin()), make_move_iterator(v1.end()),
back_inserter(v2), unique_ptr<int>());

This call to remove_copy moves all non-null unique_ptr's from v1 to v2.

(quoted from
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html )

Other candidates of non-copyable but movable objects that might benefit
from being in a container include streams and locks.

-Howard

Gabriel Dos Reis

unread,
Jul 28, 2005, 7:55:28 AM7/28/05
to
Mirek Fidler <c...@volny.cz> writes:

| > Changes to list, set, multiset, map, multimap are already in the C++0X
| > working paper which greatly reduce the assignable requirement:
| >
| > http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
|
| Thank you Howard, but that will not make it. What I really need are
| random access containers with neither assignable nor copy-constructible
| requirements.

I can say

ofstream dumpfiles[max_dump_index];

or

new ofstream[max_dump_index];

yet, not

vector<ofstream> dumpfiles;

:-(

--
Gabriel Dos Reis
g...@integrable-solutions.net

Gabriel Dos Reis

unread,
Jul 28, 2005, 7:54:31 AM7/28/05
to
"White Wolf" <wo...@freemail.hu> writes:

| In what way are indices more likely to stay valid than iterators? I mean if
| I have index #10 tored away for a vector, and then I remove stuff and have
| only 5 elements, my index becomes rogue just the same way an iterator would.
| I just do not see (or believe) that it is any less likely with indices than
| iterators.

push_back is most likely to invalid previous iterators than shrinking
a vector.

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Gabriel Dos Reis

unread,
Jul 28, 2005, 7:55:59 AM7/28/05
to
"Bob Bell" <bel...@pacbell.net> writes:

| Mirek Fidler wrote:
| > > Changes to list, set, multiset, map, multimap are already in the C++0X
| > > working paper which greatly reduce the assignable requirement:
| > >
| > > http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
| >
| > Thank you Howard, but that will not make it. What I really need are
| > random access containers with neither assignable nor copy-constructible
| > requirements. Those are kinds of things needed in GUI development.
|
| I'm curious; what kind of non-assignable, non-copy-constructible
| objects do you need to put into containers in GUI development? Or did I
| misunderstand something?

Maybe not in a GUI development, but is there a reason why I can say

ofstream dumpfiles[max];

or

new ofstream[max];

and not

vector<ofstream> dumpfiles(max);

?

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Howard Hinnant

unread,
Jul 28, 2005, 7:48:08 AM7/28/05
to
In article <3kqejmF...@individual.net>, Mirek Fidler <c...@volny.cz>
wrote:

> BTW, have you finally realised how important are composition rules there?

Nope, but I'm willing to be educated.

-Howard

Stephen Howe

unread,
Jul 28, 2005, 7:48:37 AM7/28/05
to
> In what way are indices more likely to stay valid than iterators?

If you have a vector that is full to capacity and you do a extra push_back()
that normally invalidates any iterators as the underlying memory is
reallocated. An index to an element before the push_back()'ed element is
still valid.

That is a big difference.

Stephen Howe

Russell Hind

unread,
Jul 28, 2005, 7:58:28 AM7/28/05
to
M Jared Finder wrote:
>
> Just to be pedantic, the second example is the equivalent of
> std::find_if, not of std::sort. std::sort can not be written
> efficiently using BOOST_FOREACH, as it requires mutating the underlying
> sequence in multiple passes.
>

I realise that the BOOST_FOREACH example should have gone before the
std::sort, but thats just the way they popped in to my head (don't use
BOOST_FOREACH yet in production stuff as it isn't in an official boost
release so it isn't the first thing on my mind when doing loops yet).

There certinaly are some issues with BOOST_FOREACH such as not being
able to modify the container in such a way, but as mentioned, I think
the more limiting factor is that you just don't get access to the actual
iterator which you may want to store for later use in another algorithm
or such so this will limit its use.

The only other slight issue I have with it is that if you want to modify
the values in-place, you have to remember to declare the loop variable
as a reference. I have a feeling this might be a common mistake such as

vector<int> v;
BOOST_FOREACH(int i, v)
{
i *= 2;
}

will not do anything useful.

Cheers

Russell

Russell Hind

unread,
Jul 28, 2005, 7:58:06 AM7/28/05
to
Peter Dimov wrote:
>
> The current CVS version of boost::bind (soon to be released) supports
>
> bind(&Excipient_c::GetDescription, _1) == Name
>
> and
>
> bind(&Excipient_c::GetDescription, _1) <
> bind(&Excipient_c::GetDescription, _2)
>

Thanks, I didn't realise this. That will help a lot.

Just out of interest, why isn't this sort of thing mentioned in the
latest news for 1.33.0
(http://cvs.sourceforge.net/viewcvs.py/*checkout*/boost/boost/index.htm?rev=1.230)?
My general impression is that libraries that aren't mentioned on
there, haven't changed since the previous release and therefore I don't
see an easy way for end-users to find out about updates to libraries
they already use which is a shame IMHO as we will end-up using the
library in the same way as we have been for the last year or so.

Cheers

Russell

Mirek Fidler

unread,
Jul 28, 2005, 8:41:14 AM7/28/05
to
Howard Hinnant wrote:
> In article <1122507091....@g14g2000cwa.googlegroups.com>,
> "Bob Bell" <bel...@pacbell.net> wrote:
>
>
>>Mirek Fidler wrote:
>>
>>>>Changes to list, set, multiset, map, multimap are already in the C++0X
>>>>working paper which greatly reduce the assignable requirement:
>>>>
>>>>http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
>>>
>>>Thank you Howard, but that will not make it. What I really need are
>>>random access containers with neither assignable nor copy-constructible
>>>requirements. Those are kinds of things needed in GUI development.
>>
>>I'm curious; what kind of non-assignable, non-copy-constructible
>>objects do you need to put into containers in GUI development? Or did I
>>misunderstand something?
>
>
> Well, not specific to GUI development, but certainly applicable there,
> I'm thinking vector<unique_ptr<T>> might be extremely useful.

Like this

http://upp.sourceforge.net/src$Array$en-us.html

?

Yes, that is useful, very useful.

> Other candidates of non-copyable but movable objects that might benefit
> from being in a container include streams and locks.

Exactly. What you dream of I am using :)

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 8:39:22 AM7/28/05
to
Gabriel Dos Reis wrote:
> Mirek Fidler <c...@volny.cz> writes:
>
> | > Changes to list, set, multiset, map, multimap are already in the C++0X
> | > working paper which greatly reduce the assignable requirement:
> | >
> | > http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
> |
> | Thank you Howard, but that will not make it. What I really need are
> | random access containers with neither assignable nor copy-constructible
> | requirements.
>
> I can say
>
> ofstream dumpfiles[max_dump_index];
>
> or
>
> new ofstream[max_dump_index];
>
> yet, not
>
> vector<ofstream> dumpfiles;

Well, I am saying

Array<oftream> dumpfiles;

or

ArrayMap<String, ofstream> dumpfiles_map;

for more than 7 years now.

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 8:40:53 AM7/28/05
to
>>the main difference is the lack of "copy-constructible" and "assignable"
>>requirements, I believe that it immediately cuts off at least 70% of
>>problems that I have encountered in STL.
>
>
> The really major difference is that you are addressing something
> entirely different than STL does! You are not at all addressing
> algorithms which is the primary goal of STL. You have some containers
> as has STL but the containers for STL, albeit useful, are mostly
> there as examples of sequences.

Dietmar, you was saying this before, but I think that this is not true.

STL is a library of containers and algorithms. Containers in STL are not
"examples of sequences".

> I'd bet that your containers could
> also be used with STL algorithms.

Yes, as long as types stored satisfy general STL requirements, which
often is not true. That is why I had to develop some algorithms anyway,
as they are guaranteed to work with types with different set of
requirements.

> The basic idea of STL is that
> algorithms are written once and can then be applied to all data
> structures having a reasonable representation
> (e.g. can be traversed
> in some form or have some form of random access, depending on the
> algorithm).

This might be true for some non-mutating algorithms.

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 8:38:40 AM7/28/05
to
>>BTW, have you finally realised how important are composition rules there?
>
>
> Nope, but I'm willing to be educated.

Well, nothing too complicated, it is just if I remember well, last
version of proposal gave up on composition rules.

I mean, if I have

struct Foo {
TypeWithRValueCopy a;
TypeWithNormalCopy b;
};

Here I expect compiler to generate default r-value copy constructor and
assignment operator for Foo.

This is actually similar today when compiler generates const T& and T&
copy constructors and assignment operator. When there is any subtype
with non-const copy constructor, copy constructor for whole class is
non-const as well, if there are const only, whole class copy constructor
is const too.

Mirek

Peter Dimov

unread,
Jul 28, 2005, 8:40:13 AM7/28/05
to
Gene Bushuyev wrote:

[...]

> Secondly, since the only way to pass the local context is to copy variables
> (or pointers/references) there is a lot of constructors that are copying
> stuff and it's very inflexible and a pain to extend. The real code is often

> designed by try and error process. [...]

One approach that works for me is to use a stateless function (object)
combined with boost::bind to hold local state.

> Now, lambda and binders have some of same advantages.
> But they also have problems. Compilation errors can be baffling, and it's
> practically impossible to debug nontrivial compositions.

A nontrivial composition is usually an indication that one should go
with a loop or a custom algorithm instead. It's not a global either/or
decision.

Gabriel Dos Reis

unread,
Jul 28, 2005, 10:56:56 AM7/28/05
to
Mirek Fidler <c...@volny.cz> writes:

| > I can say
| >
| > ofstream dumpfiles[max_dump_index];
| >
| > or
| >
| > new ofstream[max_dump_index];
| >
| > yet, not
| >
| > vector<ofstream> dumpfiles;
|
| Well, I am saying
|
| Array<oftream> dumpfiles;
|
| or
|
| ArrayMap<String, ofstream> dumpfiles_map;
|
| for more than 7 years now.

I know how to say

MyArray<ofstream> dumpfiles(max_dump_index);

for more than 7 years. That wasn't the point.


--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Gabriel Dos Reis

unread,
Jul 28, 2005, 10:57:54 AM7/28/05
to
Howard Hinnant <hin...@metrowerks.com> writes:

| In article <1122507091....@g14g2000cwa.googlegroups.com>,
| "Bob Bell" <bel...@pacbell.net> wrote:
|
| > Mirek Fidler wrote:
| > > > Changes to list, set, multiset, map, multimap are already in the C++0X
| > > > working paper which greatly reduce the assignable requirement:
| > > >
| > > > http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#276
| > >
| > > Thank you Howard, but that will not make it. What I really need are
| > > random access containers with neither assignable nor copy-constructible
| > > requirements. Those are kinds of things needed in GUI development.
| >
| > I'm curious; what kind of non-assignable, non-copy-constructible
| > objects do you need to put into containers in GUI development? Or did I
| > misunderstand something?
|
| Well, not specific to GUI development, but certainly applicable there,
| I'm thinking vector<unique_ptr<T>> might be extremely useful.

so that we have to allocate a vector of pointers just to hold data that
never move or are never copied? That is a simple basic thing. I hope
that is not all we can do for basic things.

| unique_ptr is like auto_ptr, but not copyable, only movable. The T
| might be a windowing abstract base class that is also not copyable, and
| perhaps not even movable.

then why can't I put the data directly in there, instead of allocating
them somewhere else and then allocate pointers for them and finally
put the pointers in the allocated non-copyable and only-moveable storage?

| Because unique_ptr is not copyable, it is safe to use within generic
| code such as containers and algorithms. If the generic code attempts a
| copy, it will fail at compile time.

that is good, but what if I do not do any of those algorithms? I
simply want to put the data in a sequence, is there anything for me?

| However because unique_ptr is
| movable, and because much generic code only requires moving instead of
| copying, significant functionality with existing (but reimplemented)
| standard containers and algorithms can be made available.
|
| vector<shared_ptr<T>> is also useful in this context. But

OK; now, I'll start the "Down with smart pointers!" movement :-)

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

w...@seed.net.tw

unread,
Jul 28, 2005, 11:06:04 AM7/28/05
to
Hendrik Schober wrote:
> Er...because you do /not/ know how to program
> in C++ unless you learned how to use the STL?

1. It is invalid to conclude that way beside untrue premise.
2. Must I use STL, or my program is less C++?
3. STL is good for many classes 'that fit its requirements'.
(many classes I am currently using simply do not fit)

Hendrik Schober

unread,
Jul 28, 2005, 11:08:53 AM7/28/05
to
Mirek Fidler <c...@volny.cz> wrote:
> [...]
> > for_each(clients.begin(), clients.end(), CancelAllSignatures());
> >
> > I think it's much better than a loop. It's obvious we will cancel all
> > signatures from all clients.
>
> Is it?

Yes.

> What CancelAllSignatures returns? Is it a functor?

I couldn't care less. No matter /how/ this is
implemented, by the first look at it, I (having
/no/ experience at all in the application domain)
see /what/ it will do.
And it's certaonly easier to read than

for( clients_t::iterator it=clients.begin(), it!=clients.end(); ++it )
CancelAllSignatures();

(I'd argue that
for_each( clients, CancelAllSignatures() );
would be even easier.)

> [...]
> Mirek

Schobi

--
Spam...@gmx.de is never read
I'm Schobi at suespammers dot org

"Coming back to where you started is not the same as never leaving"
Terry Pratchett

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Howard Hinnant

unread,
Jul 28, 2005, 11:01:52 AM7/28/05
to
In article <3ks0u6F...@individual.net>, Mirek Fidler <c...@volny.cz>
wrote:

> > I'd bet that your containers could


> > also be used with STL algorithms.
>
> Yes, as long as types stored satisfy general STL requirements, which
> often is not true. That is why I had to develop some algorithms anyway,
> as they are guaranteed to work with types with different set of
> requirements.
>
> > The basic idea of STL is that
> > algorithms are written once and can then be applied to all data
> > structures having a reasonable representation
> > (e.g. can be traversed
> > in some form or have some form of random access, depending on the
> > algorithm).
>
> This might be true for some non-mutating algorithms.

The rvalue reference proposal - library recommendations (
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html )
addresses algorithms as well as the containers. Check out the number of
mutating algorithms listed in there that do not require
CopyConstructible or CopyAssignable. They include:

remove
remove_if
unique
reverse
rotate
random_shuffle
partition
stable_partition
sort
stable_sort
partial_sort
nth_element
inplace_merge
the heap algorithms
the permutation algorithms

This proposal is backed up by a full working implementation.

-Howard

E. Rosten

unread,
Jul 28, 2005, 11:08:09 AM7/28/05
to
On Wed, 27 Jul 2005 19:19:47 -0400, White Wolf wrote:


>> Makes coding trivial, short and unlikely to be prone to hard to catch
>> errors like silently invalidated iterators or iterators going out of
>> range (ok, I know that some STL implementation provide runtime checks,
>> but thing is that these kinds of error are much less likely to happen
>> with indicies).


>
> In what way are indices more likely to stay valid than iterators? I mean if
> I have index #10 tored away for a vector, and then I remove stuff and have
> only 5 elements, my index becomes rogue just the same way an iterator would.
> I just do not see (or believe) that it is any less likely with indices than
> iterators.

If you append stuff to a std::vector, iterators become invalid, indices do
not.

[...]

> 1.) ++it and *not* it++. For gods sake: Mean what you say, and say what
> you mean!

I don't quite get what you mean here...


> 3.) Unless the container itself is being changed inside the loop, the end()
> iterator is better be saved away into a const variable before the loop.

This is no longer true:

Here's a translation unit:

#include <list>
using namespace std;

list<int> stuff;
typedef list<int>::iterator it;

int foo()
{
int a=0;
for(it i=stuff.begin(); i != stuff.end(); ++i)
a += *i;
return a;
}

int baz()
{
int a=0;
for(it i=stuff.begin(); i != stuff.end(); i++)
a += *i;
return a;
}
int bar()
{
int a=0;
it end = stuff.end();
for(it i=stuff.begin(); i != end; ++i)
a += *i;
return a;
}


Here's the assembly output from g++ -O3 (g++ 4.0.0, similar results for
3.3.5)

_Z3foov: | _Z3barv:
.LFB391: | .LFB392:
movl stuff, %edx | movl stuff, %edx
xorl %eax, %eax | xorl %eax, %eax
pushl %ebp | pushl %ebp
.LCFI2: | .LCFI0:
movl %esp, %ebp | movl %esp, %ebp
.LCFI3: | .LCFI1:
cmpl $stuff, %edx | cmpl $stuff, %edx
je .L12 | je .L4
.p2align 4,,15 | .p2align 4,,15
.L13: | .L5:
movl 8(%edx), %ecx | movl 8(%edx), %ecx
movl (%edx), %edx | movl (%edx), %edx
addl %ecx, %eax | addl %ecx, %eax
cmpl $stuff, %edx | cmpl $stuff, %edx
jne .L13 | jne .L5
.L12: | .L4:
popl %ebp | popl %ebp
ret | ret

in both cases, the code is the same. In other words, there's no need in
this case to second-guess the compiler. In the case of baz, for g++ 4.0.0,
the code is the same again. For 3.3.5, it is different, and slower:


With g++ 3.3.4 (same code (ish) but a complete program) over 100 runs, we
get:
++i
mean = 4.88
std = 0.0277

i++
mean = 5.5227s
std = 0.0289


In conclusion, on a more modern compiler, i++ and ++i have no performance
difference. On even slightly less modern compilers, there is no need to
use a temporary variable for .end(), since the compiler can figure it out
for itself.


-Ed


--
(You can't go wrong with psycho-rats.) (er258)(@)(eng.cam)(.ac.uk)

/d{def}def/f{/Times findfont s scalefont setfont}d/s{10}d/r{roll}d f 5/m
{moveto}d -1 r 230 350 m 0 1 179{1 index show 88 rotate 4 mul 0 rmoveto}
for /s 15 d f pop 240 420 m 0 1 3 { 4 2 1 r sub -1 r show } for showpage

Gabriel Dos Reis

unread,
Jul 28, 2005, 10:59:18 AM7/28/05
to
Mirek Fidler <c...@volny.cz> writes:

| Containers in STL are not "examples of sequences".

What are they?

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Carlos Moreno

unread,
Jul 28, 2005, 11:13:27 AM7/28/05
to
Mirek Fidler wrote:
>>function that could be 30 lines long with just a few if-elses and 5
>>calls to STL algorithms becomes 200 lines, then it is not easier to
>>read.
>
>
> If only that was true. I guess that very often, almost in any
> non-trivial case, hand coded loops are as long or even shorter than
> algorithms. Which in the end is what was pointed out in original post.


Agreed -- but see below.


>>class cmp_timetag
>>{
>>public:
>> bool operator() (const Packet & p1, const Packet & p2) const
>> {
>> return p1.sent_at() < p2.sent_at();
>> }
>>};
>>
>>min_element (transmit_queue.begin(),
>> transmit_queue.end(),
>> cmp_timetag());
>>
>>
>>If I need to count how many strings are longer than a given string,
>>I'd definitely use the STL:
>>
>>class longer_than
>>{
>> const string & s;
>>public:
>> longer_than (const string & s) : s(s) {}
>>
>> bool operator() (const string & item) const
>> {
>> return item.length() > s.length();
>> }
>>};
>>
>>count_if (names.begin(), names.end(), longer_than("Carlos"));
>
>
> Well, I guess you have just proved that my point above is correct...
>
> Hand coded, you would avoid using two predicate classes and reduced
> above code to much less lines.

But what really matters is the length of the "main code" -- auxiliary
functions or classes don't count in terms of making the code more
readable.

What matters is that you see *this one line* in your "main code":

count_if (names.begin(), names.end(), longer_than("Carlos"));

And that, not only is a single line (vs. four lines, without counting
the curly braces, which I personally always spend one line for each
of them, so that would make *eight* lines), but it makes it immediately
obvious that you're counting how many strings are longer than my name.
Writing the class was effort well-spent, because it was spent in writing
cleaner code.

As a subtle plus, if you have to count the number of strings longer
than a given one three times, then: 1) the effort paid off, and now
the class plus the three lines will be shorter than three hand-coded
loops. And b) there *is* the risk that the second time you will forget
to initialize the counter back to zero -- not writing code is less
error-prone than writing code! :-)

> (Also, a little bit off topic, I would store length of s rather than the
> reference to s in longer_than, but that is just me :)

Duh!!! (to clarify: I'm telling "duh" to myself :-)). Yeah, that was
certainly a nice way to give more credibility to my argument, eh? :-)

Cheers,

Carlos
--

Jeff Flinn

unread,
Jul 28, 2005, 11:10:01 AM7/28/05
to

"Russell Hind" <rh_g...@mac.com> wrote in message
news:dca14e$ppo$1$8300...@news.demon.co.uk...

> Peter Dimov wrote:
>>
>> The current CVS version of boost::bind (soon to be released) supports
>>
>> bind(&Excipient_c::GetDescription, _1) == Name
>>
>> and
>>
>> bind(&Excipient_c::GetDescription, _1) <
>> bind(&Excipient_c::GetDescription, _2)
>
> Thanks, I didn't realise this. That will help a lot.
>
> Just out of interest, why isn't this sort of thing mentioned in the
> latest news for 1.33.0
> (http://cvs.sourceforge.net/viewcvs.py/*checkout*/boost/boost/index.htm?rev=1.230)?
> My general impression is that libraries that aren't mentioned on
> there, haven't changed since the previous release and therefore I don't
> see an easy way for end-users to find out about updates to libraries
> they already use which is a shame IMHO as we will end-up using the
> library in the same way as we have been for the last year or so.

Agreed. This is a quantum improvement in simplification/readability of user
code! Awesome work Peter. Is this also part of TR1?

Thanks, Jeff

ka...@gabi-soft.fr

unread,
Jul 28, 2005, 11:13:49 AM7/28/05
to
Mirek Fidler wrote:
> > do I need to repeat the same for(xx::interator i =
> > x.begin... over and

> It is problem of iterator syntax, not of 'for'.

> > Example:

> > for_each(clients.begin(), clients.end(), CancelAllSignatures());

> > I think it's much better than a loop. It's obvious we will
> > cancel all signatures from all clients.

> Is it? What CancelAllSignatures returns? Is it a functor?

Who cares? That's the whole point. Whatever it is, it cancels
all signatures. He's given an otherwise opaque operation a
name, and that helps readability.

> > But it's good because the code reader doesn't need to know
> > the implementation detail.

> Impementation detail of what? for_each? Come on.

I think he meant the implementation detail of
CancelAllSignatures.

It's certainly not a killer argument, at least not when used
with for_each. You can (and shoul) also write:

for ( size_t i = 0 ; i < clients.size() ; ++ i ) {
cancelAllSignatures( clients[ i ] ) ;
}

Which is clearer? I'd say that the for_each version has an
edge. Only a slight one, but still an edge.

Other algorithms have a bigger edge. If I see find or find_if,
I know that a linear search is being used. If I see the loop,
it's not too hard to figure out, but I still have to figure it
out. If I see lower_bound, I know that a binary search is
used. That one takes even more time to figure out just seeing
the loop.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Carlos Moreno

unread,
Jul 28, 2005, 11:11:42 AM7/28/05
to
Dave Harris wrote:
> rodrigo...@gmail.com (Rodrigo Strauss) wrote (abridged):
>
>>I see no reason to use loops when you have a STL algorithm, everyone
>>can figure out what std::for_each does, even a non C++ programmer. Why

>>do I need to repeat the same for(xx::interator i = x.begin... over and
>>over again? It's error prone, you have the temptation of using
>>copy/past all over the code, and we all know where it ends. You can use
>>an iterator from an instance of container as the end test of another
>>instance, just to mention one common mistake.
>
>
> But that mistake is also possible with the standard algorithms. It's one
> of their design flaws.
>
> for_each( v1.begin(), v2.end(), functor() );

You *have* to agree that it is considerably less likely to make that
mistake -- this line is one order of magnitude less "visually complex"
than the line with the for loop and iterators; so, it is very unlikely
to write the above, such sinmple line that only has v1 and v2 and not
notice the mistake.

I do agree with your complaint from a conceptual point of view -- but
from the practical point of view, when writing an algorithm call like
the above, your fingers automatically type whatever dot begin() and *the
same whatever* dot end()

Carlos
--

Mirek Fidler

unread,
Jul 28, 2005, 12:02:00 PM7/28/05
to
> | > I can say
> | >
> | > ofstream dumpfiles[max_dump_index];
> | >
> | > or
> | >
> | > new ofstream[max_dump_index];
> | >
> | > yet, not
> | >
> | > vector<ofstream> dumpfiles;
> |
> | Well, I am saying
> |
> | Array<oftream> dumpfiles;
> |
> | or
> |
> | ArrayMap<String, ofstream> dumpfiles_map;
> |
> | for more than 7 years now.
>
> I know how to say
>
> MyArray<ofstream> dumpfiles(max_dump_index);
>
> for more than 7 years. That wasn't the point.

Well, maybe I got your point wrong, but please notice the absence of
"max_dump_index" in my post...

Mirek

Martin Eisenberg

unread,
Jul 28, 2005, 12:02:43 PM7/28/05
to
Howard Hinnant wrote:

> // Call f(first, first+k) for each reversible permutation of
> // [first, last) taken k items at a time.
> // "reversible" may be the wrong word here. The
> // intent is that the reverse of a permutation doesn't
> // count as a permutation, and is thus not applied.

Maybe "for_each_permutation_or_reverse"...

> for_each_circular_permutation

...and "for_each_rotation"?


Martin

--
Quidquid latine dictum sit, altum viditur.

Mirek Fidler

unread,
Jul 28, 2005, 12:10:09 PM7/28/05
to
> In conclusion, on a more modern compiler, i++ and ++i have no performance

Well, technically speaking, that is true in most cases, but OP is right
that in generic iterator algorithm, using ++i is better, because in
theory, ++ operators for some strange container iterators could be
defined in a way that really makes i++ significantly slower.

In fact, using "i++" is bad habit from my C years, anyway as I gave up
on iterators in generic code in favor of indicies, I do not care too
much anymore.

Mirek

Howard Hinnant

unread,
Jul 28, 2005, 12:15:06 PM7/28/05
to
In article <m3ek9jy...@uniton.integrable-solutions.net>,

Gabriel Dos Reis <g...@integrable-solutions.net> wrote:

> | Well, not specific to GUI development, but certainly applicable there,
> | I'm thinking vector<unique_ptr<T>> might be extremely useful.
>
> so that we have to allocate a vector of pointers just to hold data that
> never move or are never copied? That is a simple basic thing. I hope
> that is not all we can do for basic things.
>
> | unique_ptr is like auto_ptr, but not copyable, only movable. The T
> | might be a windowing abstract base class that is also not copyable, and
> | perhaps not even movable.
>
> then why can't I put the data directly in there, instead of allocating
> them somewhere else and then allocate pointers for them and finally
> put the pointers in the allocated non-copyable and only-moveable storage?

You can (assuming proposal acceptance) put your movable but non-copyable
data directly into containers. No problem. I was specifically
addressing the heterogeneous container idiom where a pointer to a base
class is used (with varying derived types at run time). Here you have
to use a pointer or reference to prevent slicing.

Of course, container<base&> is an interesting concept in its own right.
I have often thought about that, but haven't developed or proposed it.
I'm sure it is an interesting and solvable problem. I'm not sure how
useful it would be in the field. But I have overwhelming evidence that
containers of (smart) pointers to base classes are widely used.

> | Because unique_ptr is not copyable, it is safe to use within generic
> | code such as containers and algorithms. If the generic code attempts a
> | copy, it will fail at compile time.
>
> that is good, but what if I do not do any of those algorithms? I
> simply want to put the data in a sequence, is there anything for me?

Yes, no problem putting your move-only types directly into containers.

> | However because unique_ptr is
> | movable, and because much generic code only requires moving instead of
> | copying, significant functionality with existing (but reimplemented)
> | standard containers and algorithms can be made available.
> |
> | vector<shared_ptr<T>> is also useful in this context. But
>
> OK; now, I'll start the "Down with smart pointers!" movement :-)

No problem, your container<T*> will still work tomorrow as well as it
works today. :-) And if you aren't concerned about slicing, your
container<T> capability and performance is about to dramatically
increase.

-Howard

Mirek Fidler

unread,
Jul 28, 2005, 12:10:32 PM7/28/05
to
Gabriel Dos Reis wrote:
> Mirek Fidler <c...@volny.cz> writes:
>
> | Containers in STL are not "examples of sequences".
>
> What are they?
>
Ehm, pardon my english, perhaps I got bad context.

What I wanted to say is that I think that Dietmar thinks that STL
containers are unimportant and are just examples for you how to build
your own.

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 12:15:29 PM7/28/05
to
> What matters is that you see *this one line* in your "main code":
>
> count_if (names.begin(), names.end(), longer_than("Carlos"));
>
> And that, not only is a single line (vs. four lines, without counting
> the curly braces, which I personally always spend one line for each
> of them, so that would make *eight* lines), but it makes it immediately
> obvious that you're counting how many strings are longer than my name.

Well, but only as long as you know what 'longer_than' does. Otherwise it
will mean studying code at completely unrelated place and remembering
meaning of another symbol.

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 1:28:56 PM7/28/05
to

Well I am glad that somebody else finaly verified and suggested some of
concepts I was using for years.

Anyway, a the time being, this is just proposal. At the moment, there is
no other way how to do things right than to avoid STL and use something
else. Current STL algorithms do not allow storing other data than those
with CopyConstructible or CopyAssignable.

Mirek

Howard Hinnant

unread,
Jul 28, 2005, 1:29:18 PM7/28/05
to
In article <3ksapkF...@individual.net>, Mirek Fidler <c...@volny.cz>
wrote:

> > | Well, I am saying


> > |
> > | Array<oftream> dumpfiles;
> > |
> > | or
> > |
> > | ArrayMap<String, ofstream> dumpfiles_map;
> > |
> > | for more than 7 years now.
> >
> > I know how to say
> >
> > MyArray<ofstream> dumpfiles(max_dump_index);
> >
> > for more than 7 years. That wasn't the point.
>
> Well, maybe I got your point wrong, but please notice the absence of
> "max_dump_index" in my post...

Below is working code on my desk. I used ostringstream instead of
ofstream, just for ease in creating a demo with output, no other reason:

#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

struct pred
{
bool operator()(const std::ostringstream& os)
{
return os.str()[0] == '2';
}
};

int main()
{
const int n = 5;
std::vector<std::ostringstream> dumpfiles;
std::string label[5] = {"one", "two", "three", "four", "five"};
for (int i = 0; i < n; ++i)
{
std::ostringstream os;
os << (i+1) << " : ";
dumpfiles.push_back(std::move(os));
dumpfiles.back() << label[i];
}
dumpfiles.erase(
std::remove_if(dumpfiles.begin(), dumpfiles.end(), pred()),
dumpfiles.end()
);
for (std::vector<std::ostringstream>::iterator i = dumpfiles.begin(),
e = dumpfiles.end(); i < e; ++i)
{
std::cout << i->str() << '\n';
}
}

The output is:

1 : one
3 : three
4 : four
5 : five

Notes:

1. vector<ostringstream> !!!
2. No max_dump_index.
3. The ostringstream is manipulated both outside of, and inside of the
container. ostringstream state is transferred into the container.
4. std::remove_if is used to manipulate the vector<ostringstream>.
5. ostringstream is still neither CopyConstructible nor CopyAssignable.
6. Syntax is nearly indistinguishable from today's syntax. There is
very little new to learn here. Things just work.
7. ostringstream is not special. You can do the same thing with your
movable but non-copyable types. Just give them a move constructor and
move assignment (typically less difficult than coding a copy constructor
and copy assignment).

-Howard

Dietmar Kuehl

unread,
Jul 28, 2005, 1:26:57 PM7/28/05
to
Gabriel Dos Reis wrote:

> "White Wolf" <wo...@freemail.hu> writes:
>
> | In what way are indices more likely to stay valid than iterators? I
> | mean if I have index #10 tored away for a vector, and then I remove
> | stuff and have only 5 elements, my index becomes rogue just the same way
> | an iterator would. I just do not see (or believe) that it is any less
> | likely with indices than iterators.
>

> push_back is most likely to invalid previous iterators than shrinking
> a vector.

Actually, it is easy to create a container similar to vector which
does not suffer from invalidating iterators while mutating the
contents: the iterators would simply store a pointer to the container
and an index. In fact, you can even take 'std::vector' for this but
use different than the normal iterators...
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

Gabriel Dos Reis

unread,
Jul 28, 2005, 1:33:14 PM7/28/05
to
Mirek Fidler <c...@volny.cz> writes:

| > | > I can say
| > | >
| > | > ofstream dumpfiles[max_dump_index];
| > | >
| > | > or
| > | >
| > | > new ofstream[max_dump_index];
| > | >
| > | > yet, not
| > | >
| > | > vector<ofstream> dumpfiles;
| > |
| > | Well, I am saying
| > |
| > | Array<oftream> dumpfiles;
| > |
| > | or
| > |
| > | ArrayMap<String, ofstream> dumpfiles_map;
| > |
| > | for more than 7 years now.
| >
| > I know how to say
| >
| > MyArray<ofstream> dumpfiles(max_dump_index);
| >
| > for more than 7 years. That wasn't the point.
|
| Well, maybe I got your point wrong,

Indeed, twice! :-)

| but please notice the absence of "max_dump_index" in my post...

It does not matter much to the point.

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Dietmar Kuehl

unread,
Jul 28, 2005, 1:34:50 PM7/28/05
to
Mirek Fidler wrote:
>> The really major difference is that you are addressing something
>> entirely different than STL does! You are not at all addressing
>> algorithms which is the primary goal of STL. You have some containers
>> as has STL but the containers for STL, albeit useful, are mostly
>> there as examples of sequences.
>
> Dietmar, you was saying this before, but I think that this is not true.

I stick to what I said. In fact, I would even go a step further: the
real contribution of STL is entirely immaterial in C++! The heart of
STL are the concepts - even if they are, well, suboptimal (see my reply
to Scott's article for details). The next important step are the
algorithms: these are applicable to all sequences for which suitable
iterators can be created. ... and containers and iterators can be
provided however suitable: you can take the existing ones or the user
can create own sequences. Actually, a user can just create iterators
for existing containers never intended to work with STL and this
should work.

> STL is a library of containers and algorithms. Containers in STL are not
> "examples of sequences".

You mean, they are more than mere examples. Well, they are useful but
where they don't fit, a user can use his own. Actually, the is the
whole reason for STL's existence. Although it is not really spelled out
that explicitly, I think it is implicitly stated in the original
proposal from Alexander Stepanov and Meng Lee (you can see this text
from <http://www.hpl.hp.com/techreports/95/HPL-95-11.html>).

>> I'd bet that your containers could
>> also be used with STL algorithms.
>
> Yes, as long as types stored satisfy general STL requirements, which
> often is not true.

The algorithms make pretty low requirements altough the requirements
are sometimes stronger than necessary. For example, there is no
inherent reason to exclude proxy sequences when moving objects. This
is one of the suboptimal areas of the current STL and there are
different approaches under discussion to remove unnecessary restrictions.
One of them is the r-value proposal and the property map/cursor stuff
is another, both addressing different restrictions.

> That is why I had to develop some algorithms anyway,
> as they are guaranteed to work with types with different set of
> requirements.

The current concepts are flawed in several areas which is partly due
to lack of experience when they were introduced. I think the concepts
of STL should be reworked although the result will not look much
different than the current version, i.e. it will not work in terms of
indices but in terms of "cursors" which are quite similar to iterators
(actually, iterators are valid cursors but the dereference operation
is interpreted not to yield a value but rather a key). This is because
the algorithms shall be applicable to all sequences, including
sequences allowing O(1) insertions at arbitrary places which cannot
achieved while having O(1) index access, AFAIK.

Effectively, algorithms always have some inherent requirements on the
data structure, independent of how they are really implemented.
Although the current STL concepts come pretty close to a common set
of basic abstractions, the current concepts are sometimes too
restrictive. In other places, the specification of algorithms leaves
too much leeway for the implementer how to do things and thus has too
strong requirements. However, I think we are at a position where we
have more insight in the basic requirements and changing the concepts
to relax the restrictions or add more flexibility should move us in
the direction of have more universally applicable algorithms. In this
process it is probably a good idea to determine where your need to
have own algorithms was genuine and to check whether this need is
removed by the new concepts.

>> The basic idea of STL is that
>> algorithms are written once and can then be applied to all data
>> structures having a reasonable representation
>> (e.g. can be traversed
>> in some form or have some form of random access, depending on the
>> algorithm).
>
> This might be true for some non-mutating algorithms.

It should be true for all algorithms. Due to several known restrictions
with the current STL concepts, it may not be true for the current STL.
However, the STL was the first truly generic library and when it was
specified and standardized it could not even be tested at all. Thus I
think it is more than like that some restrictions are simply there
which shouldn't. I'm still convinced that the basic idea of STL is the
correct direction for algorithms and that its goal can indeed be
achieved.


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Gene Bushuyev

unread,
Jul 28, 2005, 8:45:10 PM7/28/05
to
<w...@seed.net.tw> wrote in message
news:1122556023....@g47g2000cwa.googlegroups.com...

> Hendrik Schober wrote:
>> Er...because you do /not/ know how to program
>> in C++ unless you learned how to use the STL?
>
> 1. It is invalid to conclude that way beside untrue premise.
> 2. Must I use STL, or my program is less C++?
> 3. STL is good for many classes 'that fit its requirements'.
> (many classes I am currently using simply do not fit)

Let me just add, that learning STL is a very good way of learning C++
generic programming. You, of course, can achieve with boost or other
libraries the same or better result, but it wouldn't be easier. So if a
person doesn't understand most part of STL then he probably missed a hole
lot of C++. Just a thought.

- gene

Dave Harris

unread,
Jul 28, 2005, 8:51:20 PM7/28/05
to
moreno_at_mo...@xx.xxx (Carlos Moreno) wrote (abridged):

> But what really matters is the length of the "main code" -- auxiliary
> functions or classes don't count in terms of making the code more
> readable.
>
> What matters is that you see *this one line* in your "main code":
>
> count_if (names.begin(), names.end(), longer_than("Carlos"));

If I have to write a class to carry the predicate, I might as well write a
function to do it all. So my main code would also contain one line, like:

count_longer_than( names, "Carlos" );

or whatever a sensible name would be. This is cleaner than your version.
My "other code" would be like:

int count_longer_than( const Container &c, const string &s ) {
int count = 0;
for (Container::iterator i = c.begin(); i != c.end(); ++i)
if (i->length() > s.length())
++count;
return count;
}

which is comparable in size to your functor class. There's really not a
lot in it. My way is more self-contained; it doesn't require people to
look up the specification of count_if(). And they can see more of the code
at once; there's less load on short-term memory. It's easier to step
through with the debugger. Less worry about the functor being abused, eg
returned from a function (which would be a problem because it uses
references).


> And that, not only is a single line (vs. four lines, without counting
> the curly braces, which I personally always spend one line for each
> of them, so that would make *eight* lines)

The loop doesn't need any curly braces and in my view shouldn't have any.


> but it makes it immediately
> obvious that you're counting how many strings are longer than my name.

The name of my function does that.


> As a subtle plus, if you have to count the number of strings longer
> than a given one three times, then: 1) the effort paid off, and now
> the class plus the three lines will be shorter than three hand-coded
> loops.

My one line of main code will be even shorter.


> And b) there *is* the risk that the second time you will forget
> to initialize the counter back to zero -- not writing code is less
> error-prone than writing code! :-)

I don't really see how you can make that mistake with my version. You have
to declare the count outside the loop else its scope is wrong, and you
give it an initial value as you declare it. And of course, the loop itself
is not duplicated.

For me the difference is marginal. If I and my colleagues had grown up
using count_if() I might continue to use it, but the benefits aren't
enough to justify me changing my style, still less persuading my
colleagues to.

-- Dave Harris, Nottingham, UK.

Peter Dimov

unread,
Jul 28, 2005, 8:53:37 PM7/28/05
to
Jeff Flinn wrote:

> Agreed. This is a quantum improvement in simplification/readability of user
> code! Awesome work Peter. Is this also part of TR1?

No, TR1 is pretty much final at this point. Nothing can go in, and it's
not clear what is the official way to extend it. I intend to propose
the extension for tr1::bind nonetheless. :-)

Gabriel Dos Reis

unread,
Jul 28, 2005, 8:48:50 PM7/28/05
to
Dietmar Kuehl <dietma...@yahoo.com> writes:

| Gabriel Dos Reis wrote:
|
| > "White Wolf" <wo...@freemail.hu> writes:
| >
| > | In what way are indices more likely to stay valid than iterators? I
| > | mean if I have index #10 tored away for a vector, and then I remove
| > | stuff and have only 5 elements, my index becomes rogue just the same way
| > | an iterator would. I just do not see (or believe) that it is any less
| > | likely with indices than iterators.
| >
| > push_back is most likely to invalid previous iterators than shrinking
| > a vector.
|
| Actually, it is easy to create a container similar to vector which
| does not suffer from invalidating iterators while mutating the
| contents: the iterators would simply store a pointer to the container
| and an index. In fact, you can even take 'std::vector' for this but
| use different than the normal iterators...

I know. But that is an entirely different semantics guarantee than that
of the standard vector + iterator and I was responding to the claim above.

Allwoing vector<T>::iterator to be plain "T*" and allowing "T*" to be
mapped directly to comon hardware thingy directly weaken the
usuability of vector in terms of iterators. For practical uses, I
maintain a (container, index) pair.

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Mirek Fidler

unread,
Jul 28, 2005, 8:54:01 PM7/28/05
to

Well, "transfer" is the right term here, I agree :)

> 4. std::remove_if is used to manipulate the vector<ostringstream>.
> 5. ostringstream is still neither CopyConstructible nor CopyAssignable.
> 6. Syntax is nearly indistinguishable from today's syntax. There is
> very little new to learn here. Things just work.
> 7. ostringstream is not special. You can do the same thing with your
> movable but non-copyable types. Just give them a move constructor and
> move assignment (typically less difficult than coding a copy constructor
> and copy assignment).

You still require move constructor to be part of type stored. Far too
much for me.

Code on my desk certainly fails in point 6. but allows things like

Array<Ctrl> ctrl; // Ctrl is base class for widget hierarchy
double numbers[] = { 1, 3.3, 4, 7.7, 1, 0.5 };
for(int i = 0; i < 6; i++)
if(numbers[i] == (int)numbers[i])
ctrl.Create<EditInt>() <<= numbers[i];
else
ctrl.Create<EditDouble>() << numbers[i];
Sort(ctrl, StdDataLess());
TopWindow dlg;
for(int i = 0; i < ctrl.GetCount(); i++)
dlg.Add(ctrl[i].LeftPos(0, 200).TopPos(i * 20, 20);
dlg.SetRect(0, 0, 200, 200);
dlg.Run();

... displays dialog with int and double input fields, sorted by numbers
contained in them.

All that is required for types stored in Array<Ctrl> is that they are of
type Ctrl or derived from it and have default constructor (default
constructor requirement holds true only for Create method, can be avoided).

Of course, I could store ostringstream into Array as easily without
difficulity and without std::move. E.g. (direct equivalent):

Array<ostringstream> x;
for(int i = 0; i < n; i++) {
x.Add() << (i + 1) << " : ";
x.Top() << label[i];
}

Does not require ostringstream to be movable (in your terminology). It
just has to have default constructor, something easily satisfied by
virtually any class. But even without it, Array is still applicable.

And note that it is significantly shorter code as well...

Mirek

Gabriel Dos Reis

unread,
Jul 28, 2005, 8:54:24 PM7/28/05
to
Dietmar Kuehl <dietma...@yahoo.com> writes:

[...]

| > STL is a library of containers and algorithms. Containers in STL are not
| > "examples of sequences".
|
| You mean, they are more than mere examples. Well, they are useful but
| where they don't fit, a user can use his own.

One can argue about the value of having them standard if the library
and language constructs can not make them composable for the basic things
and people have tp continue to revinvent the wheel after so much of
excitement! Howard reports a bridge in form of std::move. That is
encouraging news. I'm looking forward to seeing better integration.
And yes, it is possible to reduce the neds to wheel reinvention.

I understand the view of "where they don't fit, a user can use his
own"; but I fear that it is being carried way to far, with the
tendency of "expert only" language and library that contains only
"cool stuff" and leaves the basic, frequent things to the user.

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Allan W

unread,
Jul 28, 2005, 8:52:43 PM7/28/05
to
Howard Hinnant wrote:
> The rvalue reference proposal - library recommendations (
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html )
> addresses algorithms as well as the containers. Check out the
> number of mutating algorithms listed in there that do not require
> CopyConstructible or CopyAssignable. They include:

Excuse me for responding without having first read the library
proposal that you linked to first, but...

> remove
> remove_if
> unique
> reverse
> rotate
> random_shuffle
> partition
> stable_partition
> sort
> stable_sort
> partial_sort
> nth_element
> inplace_merge
> the heap algorithms
> the permutation algorithms

...but how do you implement any of these... especially the shuffles and
sorts... without being able to move the elements from one location to
another?

The answer has to be std::swap, right? But the general implementation
of
swap requires T to be assignable.

If you aren't trying to prove some point, would you ever create a class
that can be swapped but not copied or assigned? Have you ever done this?

Rodrigo Strauss

unread,
Jul 28, 2005, 8:58:00 PM7/28/05
to
>> for_each(clients.begin(), clients.end(), CancelAllSignatures());
>> I think it's much better than a loop. It's obvious we will cancel all
>> signatures from all clients.

>Is it? What CancelAllSignatures returns? Is it a functor?

Yes, its a functor.

>> But it's good because the code reader
>> doesn't need to know the implementation detail.

>Impementation detail of what? for_each? Come on.

No, implementation details of the functor CancelAllSignatures. It's
clear all signatures will be cancelled, but the implementation is
"hidden". IMO it makes the code clear.

Rodrigo Strauss

Dave Harris

unread,
Jul 28, 2005, 9:02:16 PM7/28/05
to
moreno_at_mo...@xx.xxx (Carlos Moreno) wrote (abridged):
> > for_each( v1.begin(), v2.end(), functor() );
>
> You *have* to agree that it is considerably less likely to make that
> mistake -- this line is one order of magnitude less "visually complex"
> than the line with the for loop and iterators; so, it is very unlikely
> to write the above, such sinmple line that only has v1 and v2 and not
> notice the mistake.

I don't find it a common mistake either way. For me the manual loop is
more obviously wrong because it is more self-contained:

for (iterator i = v1.begin(); i != v2.end(); ++i)
use(i);

we can see immediately that the iterator is taken from one container and
compared with another. With for_each() we have to know how its arguments
are used. Some algorithms, eg copy(), are /supposed/ to take iterators
from different collections.

I'm not convinced by the "visually complex" argument. The for_each()
version puts the predicate within the function call, which adds a lot of
complexity. The manual loop moves it onto a separate line so it is not all
mashed in together. We can consider them separately, which reduces the
complexity.


> I do agree with your complaint from a conceptual point of view -- but
> from the practical point of view, when writing an algorithm call like
> the above, your fingers automatically type whatever dot begin() and *the
> same whatever* dot end()

The same automatic typing happens with the manual loop. Indeed, it is one
of the reasons that for_each() doesn't add much value for me.

-- Dave Harris, Nottingham, UK.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Mirek Fidler

unread,
Jul 28, 2005, 9:03:08 PM7/28/05
to
> I stick to what I said. In fact, I would even go a step further: the
> real contribution of STL is entirely immaterial in C++!

Hehe, I think we can agree on that :)

> to lack of experience when they were introduced. I think the concepts
> of STL should be reworked although the result will not look much
> different than the current version, i.e. it will not work in terms of
> indices but in terms of "cursors" which are quite similar to iterators
> (actually, iterators are valid cursors but the dereference operation
> is interpreted not to yield a value but rather a key).

Well, actually, at the moment I have still based my algorithms mostly on
iterator concept (just with different set of requirements). But I must
say that right at the moment, I am starting to think it is not that good
idea - I have encountered a several situations where this concept leads
to inherently ineffective code or iterators do not provide sufficient
interface to create required algorithm at all.

That is why I have started thinking about swithing to some different
approach, perhaps based on some general container interface. In the end,
you can always transform begin - end pair to the container
representation....

> This is because
> the algorithms shall be applicable to all sequences, including
> sequences allowing O(1) insertions at arbitrary places which cannot
> achieved while having O(1) index access, AFAIK.

Ah, famous list argument :)

Well, first of all, number of really usefull algorithms applicable on
list is quite limited. (But this opinion is likely to be influenced by
my opposition to implement trivial loops by algorithms and functors).

Anyway, more importantly, I believe that O(1) insertions advantage is a
myth. Before you can apply such insertion, you have to find place where
to insert and list iteration is much slower than vector iteration.

I am afraid that number of real world problems where list has any
advantage is pretty close to zero. I have not found any real use for
list container since I have started using C++ (for 12 years). (BTW, this
does not apply at list data structure, just as container it is useless).

> the direction of have more universally applicable algorithms. In this
> process it is probably a good idea to determine where your need to
> have own algorithms was genuine and to check whether this need is
> removed by the new concepts.

OK. Most important differences are:

a) my algorithms allow T to have implicit move copy (a = b; - b can now
be "moved", well, in my terminology, "picked")

b) sort requires just iter_swap and compare predicate.

And well, I do provide syntax sugar so that algorithms can be applied on
entire container, but that can be done for STL easily (but makes me
wonder why it was not? what is so special on typing "foo.begin(),
foo.end()" all over again?)

> which shouldn't. I'm still convinced that the basic idea of STL is the
> correct direction for algorithms and that its goal can indeed be
> achieved.

Well, depends on what you think that basic idea of STL is...

If it is idea of containers that do not require elements stored to be
derived from some "Object", I agree.

If it is idea that algorithms should be separated from containers
universal, I agree.

With iterators as universal sequence representation, I am not so sure,
but I can agree to some degree, at least before I will know some better
solution.

With STL set of requirements and containers I am very unsatisfied.

Mirek

Mirek Fidler

unread,
Jul 28, 2005, 8:58:22 PM7/28/05
to
Dietmar Kuehl wrote:
> Gabriel Dos Reis wrote:
>
>
>>"White Wolf" <wo...@freemail.hu> writes:
>>
>>| In what way are indices more likely to stay valid than iterators? I
>>| mean if I have index #10 tored away for a vector, and then I remove
>>| stuff and have only 5 elements, my index becomes rogue just the same way
>>| an iterator would. I just do not see (or believe) that it is any less
>>| likely with indices than iterators.
>>
>>push_back is most likely to invalid previous iterators than shrinking
>>a vector.
>
>
> Actually, it is easy to create a container similar to vector which
> does not suffer from invalidating iterators while mutating the
> contents: the iterators would simply store a pointer to the container
> and an index. In fact, you can even take 'std::vector' for this but
> use different than the normal iterators...

Sure. But it solves only invalidating of iterator, not out-of range problem.

Mirek

Andrew Ward

unread,
Jul 28, 2005, 9:15:49 PM7/28/05
to
Hendrik Schober wrote:
> w...@seed.net.tw <w...@seed.net.tw> wrote:
>
>>[...]
>>Let's say, If I already know how to program in C++, why
>>bother spending longer time to learn STL [...]

>
>
> Er...because you do /not/ know how to program
> in C++ unless you learned how to use the STL?
>

That is just plain wrong. I would guess that in fact most C++
programmers do not use the STL. Think about the 100s of thousands of C++
programmers who use MFC, ATL, VCL, Qt, CLI...
All of these libraries provide alternatives to the STL and most were
around long before the STL became standard. Just because someone picks a
third-party library and decides to make it part of the C++ standard does
not mean people have to use it.

Andrew.

Peter Dimov

unread,
Jul 28, 2005, 9:12:28 PM7/28/05
to
Russell Hind wrote:
> Peter Dimov wrote:
> >
> > The current CVS version of boost::bind (soon to be released) supports
> >
> > bind(&Excipient_c::GetDescription, _1) == Name
> >
> > and
> >
> > bind(&Excipient_c::GetDescription, _1) <
> > bind(&Excipient_c::GetDescription, _2)
>
> Thanks, I didn't realise this. That will help a lot.
>
> Just out of interest, why isn't this sort of thing mentioned in the
> latest news for 1.33.0
> (http://cvs.sourceforge.net/viewcvs.py/*checkout*/boost/boost/index.htm?rev=1.230)?

It should be now. :-)

Gabriel Dos Reis

unread,
Jul 28, 2005, 10:35:51 PM7/28/05
to
Mirek Fidler <c...@volny.cz> writes:

| Dietmar Kuehl wrote:
| > Gabriel Dos Reis wrote:
| >
| >
| >>"White Wolf" <wo...@freemail.hu> writes:
| >>
| >>| In what way are indices more likely to stay valid than iterators? I
| >>| mean if I have index #10 tored away for a vector, and then I remove
| >>| stuff and have only 5 elements, my index becomes rogue just the same way
| >>| an iterator would. I just do not see (or believe) that it is any less
| >>| likely with indices than iterators.
| >>
| >>push_back is most likely to invalid previous iterators than shrinking
| >>a vector.
| >
| >
| > Actually, it is easy to create a container similar to vector which
| > does not suffer from invalidating iterators while mutating the
| > contents: the iterators would simply store a pointer to the container
| > and an index. In fact, you can even take 'std::vector' for this but
| > use different than the normal iterators...
|
| Sure. But it solves only invalidating of iterator, not out-of range problem.

Isn't that one already solved?

--
Gabriel Dos Reis
g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Wu Yongwei

unread,
Jul 29, 2005, 5:48:37 AM7/29/05
to
Mirek Fidler wrote:
> Anyway, more importantly, I believe that O(1) insertions advantage is a
> myth. Before you can apply such insertion, you have to find place where
> to insert and list iteration is much slower than vector iteration.
>
> I am afraid that number of real world problems where list has any
> advantage is pretty close to zero. I have not found any real use for
> list container since I have started using C++ (for 12 years). (BTW, this
> does not apply at list data structure, just as container it is useless).

I am really puzzled. What can be the more natural representation of
the list data structure than the list container?

I use std::list a lot, for different purposes. Very often it is just a
container of pointers to maintain some objects' lifetime. Other
containers can do too, but I find list the easiest and the fastest.

Best regards,

Yongwei

Wu Yongwei

unread,
Jul 29, 2005, 5:53:06 AM7/29/05
to
Mirek Fidler wrote:
> Well I am glad that somebody else finaly verified and suggested some of
> concepts I was using for years.
>
> Anyway, a the time being, this is just proposal. At the moment, there is
> no other way how to do things right than to avoid STL and use something
> else. Current STL algorithms do not allow storing other data than those
> with CopyConstructible or CopyAssignable.

What makes you uncomfortable with Container<Object*> or
Container<shared_ptr<Object> >? Please name some places where your NTL
containers have an advantage over them.

Best regards,

Yongwei

Dietmar Kuehl

unread,
Jul 29, 2005, 6:16:47 AM7/29/05
to
Mirek Fidler wrote:
>> I know how to say
>>
>> MyArray<ofstream> dumpfiles(max_dump_index);
>>
>> for more than 7 years. That wasn't the point.
>
> Well, maybe I got your point wrong, but please notice the absence of
> "max_dump_index" in my post...

I think you got Gabriel's point indeed wrong: I think, he is saying
that he nows how to create his own container dealing with objects of
type 'ofstream'. It is, however, an embarrassment that the standard
containers don't do the same. The presence or absence of the initial
container size is quite irrelevant in this context.

However, why focus on containers anyway? Scott specifically asked
about the *algorithms*! It is much more important to makes these
universally usable anyway: everybody can write his own sequences.
I, at least, are quite used to creating my own sequences, most often
in the form of input iterators giving some view of containers, and
to use them with algorithms. In this context I find too often that
the current STL concepts are too restrictive.


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Dietmar Kuehl

unread,
Jul 29, 2005, 6:17:08 AM7/29/05
to
Mirek Fidler wrote:
> Gabriel Dos Reis wrote:
>> Mirek Fidler <c...@volny.cz> writes:
>>
>> | Containers in STL are not "examples of sequences".
>>
>> What are they?
>>
> Ehm, pardon my english, perhaps I got bad context.

Well, maybe Gabriel understands the containers as examples as I do
and his question is genuinely "what are they, if not [only] examples?"

> What I wanted to say is that I think that Dietmar thinks that STL
> containers are unimportant

... from the perspective of STL. Users may have and typically do have
a different view on these containers as is clearly demonstrated by
repeated requests to add certain containers. Most of these are easy
to write and it is easier to just create them than to complain about
their absence. After all, all a container has to do is to cope with
the stuff immediately related to containment (element maintenance and
access). The algorithms operating on containers are readily available
for user defined containers.

> and are just examples for you how to build your own.

No, this is not at all what I think nor, hopefully, said! Of course,
you can create your containers the way the STL containers are build.
However, you can use drastically different approaches, too. The
containers are examples of different sequences, all applicable to
STL algorithms. The different selections, e.g. array base and node
based, demonstrate that the algorithms work with all of them. I guess,
even 'std::bitset' and 'std::vector<bool>' where included in the
believe that it demonstrates that STL algorithms can operate on them.
Unfortunately, it turned out that the STL concepts are flawed and e.g.
do not allow proxy sequences. This is, of course, something which can
be fixed by changing the basic concepts. There was a proposal relaxing
the current requirements but I think that it is understood that the
separation of traversal and access yields a better fix.

As I said in another article: STL is the right way to go. It needs,
however, some tweaking with its concepts to become universally
applicable (plus some usability stuff...).


--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

It is loading more messages.
0 new messages