C++ Frequently Questioned Answers

768 views
Skip to first unread message

Yossi Kreinin

unread,
Oct 26, 2007, 4:27:19 AM10/26/07
to
The C++ FQA Lite is available here: http://yosefk.com/c++fqa
It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite

While certainly not off-topic for comp.lang.c++[.moderated], the FQA
can make a flame bait. Apparently, a good way to prevent flame wars is
to have the discussion in a moderated newsgroup (I have a recent
experience with a weakly moderated forum, and it was suboptimal). You
can also e-mail me privately (if you're human, you'll find the address
at the site). One thing I'm definitely interested in is whether I got
any facts wrong, and if I did, which ones.

The FQA is fairly large (about half the size of the FAQ). The
"summary" pages are:

http://yosefk.com/c++fqa/defective.html
http://yosefk.com/c++fqa/picture.html

Reading the disclaimers can prevent some of the non-technical replies:

http://yosefk.com/c++fqa/disclaimers.html

-- Yossi


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

Alex Shulgin

unread,
Oct 28, 2007, 2:01:52 PM10/28/07
to
Yossi Kreinin wrote:
> The C++ FQA Lite is available here: http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite
>
> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.

DISCLAIMER: I haven't read all of your FQA--there is just too many of
them.

Although it seems you did a fair amount of research, to me your post
is not much more than a very sophisticated way of saying "C++ SUCKS"
on a moderated C++ newsgroup. :-)

>From your words I can guess that you have had pretty weird experience
with C++ on a large project... and somehow you have expected much of
the "features" the language have never declared to have: garbage
collection, built-in run-time checks, reflection, etc; Features one
would normally expect from a _very_ high level language like Python,
Ruby, Lisp, etc; but not C++.

So basically, instead of blaming yourself (or whoever is responsible)
for poor language/task choice you try to blame C++... Duh!

To be more specific, from http://yosefk.com/c++fqa/defective.html:

"No compile time encapsulation"

This item implies that you have to ship the implementation class
declaration to clients, which is not necessarily true. Have you ever
heard about abstract classes ("interfaces")? (Obviously, this won't
work with templates, but you have to provide the definitions in that
case too.)

"Outstandingly complicated grammar"

Maybe, you have your point here.

"No run time encapsulation"

See above: no built-in run-time checks.

"No binary implementation rules"

Frankly, I don't see your point here. You have tried to link g++-
compiled libraries with msvc-compiled code, or what exactly?

"Very complicated type system"

I'll quote that a bit to bring in some context:

> For example, if your function accepts const std::vector<const
> char*>& (which is supposed to mean "a reference to an immutable
> vector of pointers to immutable built-in strings"), and I have a
> std::vector<char*> object ("a mutable vector of mutable built-in
> strings"), then I can't pass it to your function because the types
> aren't convertible. You have to admit that it doesn't make any
> sense, because your function guarantees that it won't change
> anything, and I guarantee that I don't even mind having anything
> changed, and still the C++ type system gets in the way and the only
> sane workaround is to copy the vector.

Not really. You can rewrite your function into a function template
which will accept a range instead of `std::vector< whatever >&':

template< typename Iter > void func2(Iter begin, Iter end);

Of course, you loose that prominent `const*' in declaration, but as
long as implementation complies with the given pact (not to modify
whatever iterators "point to"), I think it's pretty OK.

Anyway, I don't recall myself applying cv-qualifiers to the type of
contained elements in a sensible and useful way. Anyone?

"Defective exceptions"

> ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.

Maybe, if you prefer to write everything from scratch instead of
trying to stick to standard library. Personally, I have never had
that kind of problem.


Now... I'm kinda tired writing all that stuff. Do you want me to
continue?


--
Kind regards,
Alex

Walter Bright

unread,
Oct 28, 2007, 9:26:22 PM10/28/07
to
Alex Shulgin wrote:
> From your words I can guess that you have had pretty weird experience
> with C++ on a large project... and somehow you have expected much of
> the "features" the language have never declared to have: garbage
> collection, built-in run-time checks, reflection, etc; Features one
> would normally expect from a _very_ high level language like Python,
> Ruby, Lisp, etc; but not C++.

It is not necessary to have a very high level language to have these
features (after all, Pascal had runtime checks decades ago). C++ does
have some compile time reflection (sizeof, offsetof, and the various
traits templates), and some runtime reflection (dynamic_cast, rtti).

> So basically, instead of blaming yourself (or whoever is responsible)
> for poor language/task choice you try to blame C++... Duh!

While it's pretty obvious that Standard C++ doesn't have gc, many of the
issues with C++ don't become apparent until you're fairly skilled with
it. The lack of transitive const comes to mind.

> "No binary implementation rules"
>
> Frankly, I don't see your point here. You have tried to link g++-
> compiled libraries with msvc-compiled code, or what exactly?

I think he means ABI, Application Binary Interface. While a lack of this
allows C++ compiler vendors to innovate with it (and they have done so),
the downside is that objects compiled with one compiler cannot be linked
with objects compiled with another compiler.

> "Defective exceptions"
>
>> ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.
>
> Maybe, if you prefer to write everything from scratch instead of
> trying to stick to standard library. Personally, I have never had
> that kind of problem.

Exception safety isn't hard to achieve with RAII techniques if the
things that must be unwound are independent, or nested. Achieving
exception safety where A, B, C, and D must either all happen or none
happen is much more difficult.

For more information:

http://www.digitalmars.com/d/exception-safe.html

http://www.ddj.com/cpp/184403758

"Item 29: Strive for exception-safe code" in Effective C++ Third
Edition, pg. 127 by Scott Meyers

-----
Walter Bright
http://www.digitalmars.com
Digital Mars C, C++, D programming language compilers

Alf P. Steinbach

unread,
Oct 28, 2007, 9:22:37 PM10/28/07
to
* Yossi Kreinin:

> The C++ FQA Lite is available here: http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite

A Frequently Questioned Answers is a very good idea, and I commend the
effort you've put into this.


> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.

Well, most of it. ;-)

One main high level issue that the FAQ gets wrong is the purpose of a
programming language. In general it's a good idea to use the most
appropriate language for a task, but your FQA seems to assume that
somehow everything has to be done in C++. OK, it can be done, and more
to the point it is often done, but it's just stupid to use C++ for
everything (as your FQA implicitly points out a thousand times or more),
and that's not a fault of the language: it's a fault of the user.

One main fact the FQA got wrong is that C++ doesn't have garbage
collection, in the sense that it doesn't support garbage collection.
Garbage collectors such as the Boehm collector work well. However, for
many C++ programmers garbage collection is not desirable, because while
it can ensure that memory is always reclaimed, it leads to other problems.


>From the FQA:
> And what if you make a mistake, one single mistake with all these deletions?
> Either you'll run out of memory, or your program will crash, or it will
> corrupt its own data. Which is why many experienced C++ programmers do
> everything to avoid explicit calls to new. Instead, they use RAII - have some
> constructor do the new and the destructor do the delete. Which eventually
> leads to lots of unnecessary copying - you can't point to some data inside
> a data structure since the data structure may be about to die, and it will
> kill all its data whether or not someone points to it. So you have to make a
> copy of that data, and keep believing that manual memory management is what
> makes your C++ programs so fast.

Note that the FAQ argument here is that in some other language you are
encouraged to access invalid data, "zombie" objects that have been
logically destroyed but live on, and that somehow that is a good thing.

Good C++ programmers generally think it's not a good thing, because
correctness is most important.

They think it's bad, and use RAII to avoid it. The point being that
with proper RAII, if an object is still alive, then its class invariant
still holds. And that that class invariant is a strong one giving very
practically useful guarantees, not a watered-down one "this object
either provides the following guarantees, or perhaps not, depending on
zombiness level" suitable for gc zombie objects.


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Eugene Gershnik

unread,
Oct 29, 2007, 5:00:22 PM10/29/07
to
Alex Shulgin wrote:
>
> "Very complicated type system"
>
> I'll quote that a bit to bring in some context:
>
>> For example, if your function accepts const std::vector<const
>> char*>& (which is supposed to mean "a reference to an immutable
>> vector of pointers to immutable built-in strings"), and I have a
>> std::vector<char*> object ("a mutable vector of mutable built-in
>> strings"), then I can't pass it to your function because the types
>> aren't convertible. You have to admit that it doesn't make any
>> sense, because your function guarantees that it won't change
>> anything, and I guarantee that I don't even mind having anything
>> changed, and still the C++ type system gets in the way and the only
>> sane workaround is to copy the vector.

The interesting thing is the OP chooses to criticise something that is much
harder or impossible to do in languages he seems to prefer. How do you say
'immutable Foo' in Java or C#? You can't and are left with coding guidelines
like 'make your classes [always] immutable' (see for example Effective Java
book) which are there for a good reason. The same reason C++ has const in
the language.

When you do try to make things immutable but still need to modify them from
time to time you hit exactly the same problem as in his C++ example. There
are three known ways of having 'immutable Foo' in popular languages.

One is to have two distinct classes, like Java's String and StringBuffer.
One of them is immutable, another is not and they can be freely converted to
each other. This is the 'recommended' approach but if you do this you will
quickly discover that you can't pass List<StringBuffer> to a function
expecting List<String>. Just like in C++ only worse becasue there is no
solution to it.

Another approach is to have something like (in C++ notation)

struct ImmutableFoo
{
virtual int GetValue() = 0;
};

struct MutableFoo : ImmutableFoo
{
virtual void SetValue(int val) = 0;
};

struct Foo : MutableFoo
{
int GetValue();
void SetValue(int val);
};

Unfortunately, you still cannot pass List<Foo> to a function that expectes
List<ImmutableFoo> (for the same reason arrays are not polymorfic in C++).
However, now it is possible to do something about this and the solution is
along the lines suggested for C++. You make the function a generic that
accepts List<anything derived from ImmutableFoo>.
This approache to constness sort of works but badly. It is intrusive,
doesn't scale well (what if Foo concept itself is polymorphic?) and so is
rarely used.

Finally you can do what Java collections do, namely provide a wrapper that
fails mutating methods at runtime. Something like (in C++ notation again)

struct Foo
{
virtual int GetValue() = 0;
virtual void SetValue(int val) = 0;
};

struct FooImpl : Foo
{
int GetValue();
void SetValue(int val);
};

struct ImmutableFoo : Foo
{

int GetValue()
{ return impl.GetValue(); }

void SetValue(int val)
{ throw some_exception(); }

Foo impl;
};


This "works" since you will only have List<Foo> as the parameter to any
function. The price you pay is detecting violations of const correctness
only at runtime.

Now which language's type system makes things more complicated?

--
Eugene

Alex Shulgin

unread,
Oct 29, 2007, 5:02:10 PM10/29/07
to
On Oct 29, 4:26 am, Walter Bright <wal...@digitalmars-nospamm.com>
wrote:
> Alex Shulgin wrote:
[snip]

> >> ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.
>
> > Maybe, if you prefer to write everything from scratch instead of
> > trying to stick to standard library. Personally, I have never had
> > that kind of problem.
>
> Exception safety isn't hard to achieve with RAII techniques if the
> things that must be unwound are independent, or nested. Achieving
> exception safety where A, B, C, and D must either all happen or none
> happen is much more difficult.
>
> For more information:
>
> http://www.digitalmars.com/d/exception-safe.html
>
> http://www.ddj.com/cpp/184403758
>
> "Item 29: Strive for exception-safe code" in Effective C++ Third
> Edition, pg. 127 by Scott Meyers

So we are talking about transactions... Anyway I would expect
transactional programming to be hard to get right in any language
which does not provide standard means for this.

Nice links. :-)


--
Regards,
Alex

David Abrahams

unread,
Oct 29, 2007, 5:41:22 PM10/29/07
to

on Sun Oct 28 2007, "Alf P. Steinbach" <alfps-AT-start.no> wrote:

> * Yossi Kreinin:
>> The C++ FQA Lite is available here: http://yosefk.com/c++fqa
>> It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite
>
> A Frequently Questioned Answers is a very good idea, and I commend the
> effort you've put into this.
>
>
>> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
>> can make a flame bait. Apparently, a good way to prevent flame wars is
>> to have the discussion in a moderated newsgroup (I have a recent
>> experience with a weakly moderated forum, and it was suboptimal). You
>> can also e-mail me privately (if you're human, you'll find the address
>> at the site). One thing I'm definitely interested in is whether I got
>> any facts wrong, and if I did, which ones.
>
> Well, most of it. ;-)

Frankly, I was surprised at how many things the "FAQ Lite" gets
wrong, making it an extremely easy target. For example,


[35.6] What is "genericity"?

Yet another way to say, "class templates."

http://parashift.com/c++-faq-lite/templates.html#faq-35.6

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

ap...@student.open.ac.uk

unread,
Oct 30, 2007, 8:41:41 AM10/30/07
to
On 26 Oct, 08:27, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> The C++ FQA Lite is available here:http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ -http://parashift.com/c++-faq-lite

>
> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.
>
> The FQA is fairly large (about half the size of the FAQ).

Yes it is. In order to make it easier to navigate could you put fwd
and backward arrows on each page please? A top button would be useful
as well.

I realise that alot of work has gone into this so please don't take
this the wrong way: after reading for a bit it seems to me like a long
rant against C++. I think many of the criticisms are valid but are
phrased in an aggressive way. It makes it appear the people
responsible for C++ are stupid or irresponsible, which IMO is not the
case.

Would you be ammenable to suggestions from this NG that would tone
some of the entries down a bit?

-Andrew Marlow

Yossi Kreinin

unread,
Oct 30, 2007, 3:51:17 PM10/30/07
to
On Oct 30, 2:41 pm, ap...@student.open.ac.uk wrote:
> I think many of the criticisms are valid but are
> phrased in an aggressive way. It makes it appear the people
> responsible for C++ are stupid or irresponsible, which IMO is not the
> case.

About "aggression": http://yosefk.com/c++fqa/faq.html#faq-6
As the disclaimers section states, I didn't want to personally offend
anybody.

>
> Would you be ammenable to suggestions from this NG that would tone
> some of the entries down a bit?
>

Definitely, if you can point out the places which make actual people
appear stupid or make any derogatory statements (maybe e-mail is a
better way to talk about the details). I will probably not remove the
claims about how marketing forces shaped C++ (which I think are the
fiercely phrased ones), because of quotes like this (http://
www.research.att.com/~bs/dne.html):

"Within C++, there is a much smaller and cleaner language struggling
to get out," which he says "would ... have been an unimportant cult
language."

.../and/ of course because of quotes like this (http://
www.research.att.com/~bs/bs_faq.html#C++success):

"the chance of success was affected by marketing clout, which I did
not have."

P.S. I'll try to answer the rest of the meaningful criticism in the
evening (I've quit all online discussions in non-moderated newsgroups
though). I'm publishing factual corrections at this page (which also
has my definition of "factual" in this context):

http://yosefk.com/c++fqa/web-vs-fqa.html

If you want to get a reply from me, e-mail is the most effective way:
http://yosefk.com

-- Yossi

LR

unread,
Oct 30, 2007, 9:58:23 PM10/30/07
to
Yossi Kreinin wrote:
> The C++ FQA Lite is available here: http://yosefk.com/c++fqa

I haven't read the whole thing, but what I have read seems very interesting.

But I'm not quite sure that I understand the point you're trying to make.

The FQA contains at least one FAQ (in the literal sense, rather than the
sense that FAQ is used for Answers to Frequently Asked Questions), in
FQA 23.6 http://yosefk.com/c++fqa/inheritance-mother.html#fqa-23.6
"How about trying another programming language?"

Could this be what you're driving at? If so, what do you have in mind?

LR

Yossi Kreinin

unread,
Oct 31, 2007, 12:39:37 PM10/31/07
to
On Oct 29, 11:00 pm, "Eugene Gershnik" <gersh...@hotmail.com> wrote:
>
> When you do try to make things immutable but still need to modify them from
> time to time you hit exactly the same problem as in his C++ example.

Some time after I wrote about the problems with const, which tended to
get in my way when I had classes with member pointers, I learned that
apparently D improved const. I didn't know about a language that did
it before that.

However, I don't think it's very important to get const right. The
ultimately important thing is usability. From my experience, a
language without const is sure usable. Possibly, having a working
const in the language can be even better. But having the C++ kind of
const, which breaks when you have levels of indirection, is suboptimal
in terms of usability - you never get the typing quite right, and the
compiler keeps complaining. And avoiding const will not work well,
either (you won't be able to do things like func(ClassName(args)), for
example, and if you need libraries that use const, it will get worse):

http://yosefk.com/c++fqa/const.html#fqa-18.1

One counter argument is that the C++ approach still allows for more
compile time checking compared to having no const at all. I think that
compile time checking is overrated in this case compared to the
usability problems, including the clarity of error messages (consider
the ones you get if you say "map::iterator p=m.begin()" when m is a
const map). There are things you'd obviously not try to represent in a
static type system (such as subsets of natural numbers - "this
argument must be an even number, this one must be a prime number, and
this one must be a number representing a Turing machine that halts").
I think that "mutability" gets close to the edge here (I'm sincerely
not sure at which side of the edge it is); again, I didn't try to work
with "better const", only with the C++ kind.

As to the idea to convert void func(const std::vector<const T*>&) to
template<class It> void func(It b, It e): this forces me to make func
a template, which moves the source code to header files slowing down
the build and further complicates the error messages, etc., opening a
can of warms unrelated to this sub-thread. I'm aware of this option
and find it completely unacceptable. As to the idea to avoid the const
qualification of vector the element type - this may work, or it may
end with a const_cast somewhere else, since you might fill the vector
with elements which you get from functions returning const objects.


--

Maciej Sobczak

unread,
Oct 31, 2007, 7:36:20 PM10/31/07
to
On 31 Pa , 17:39, Yossi Kreinin <yossi.krei...@gmail.com> wrote:

> However, I don't think it's very important to get const right. The
> ultimately important thing is usability. From my experience, a
> language without const is sure usable.

>From some other exprience, a language without any static type system
is also surely usable. Should we criticize those that have a static
type system?

> One counter argument is that the C++ approach still allows for more
> compile time checking compared to having no const at all.

I have found recently (or better: I have came to appreciate) that
constness is essential for code clarity - to the point that I would
even endorse having const as a default (!) with some 'variable'
modifier (or just 'mutable' as an existing keyword) or such to mark
those objects that are actually allowed to change.

Difference in expectations.

> I think that
> compile time checking is overrated in this case compared to the
> usability problems, including the clarity of error messages

The clarity of error message has nothing to do with whether constness
is a good idea or not. It's a quality of implementation issue. You can
say that 100% of compilers are crappy when it comes to error messages
(and you might be surprised how many of C++ programmers would actually
agree), but that is still far from saying that the language itself
sucks.

> There are things you'd obviously not try to represent in a
> static type system

> I think that "mutability" gets close to the edge here

Mutability is one of the simplest things we can assert about any given
object. Much simpler than, say, type substitution. Why not using the
type system for such a simple thing?

> again, I didn't try to work
> with "better const", only with the C++ kind.

Try the Ada one.
No, it's not "better".


> As to the idea to convert void func(const std::vector<const T*>&) to
> template<class It> void func(It b, It e): this forces me to make func
> a template

It might only contain a loop that calls void funcElem(const T *) for
each iterator value in the range, and funcElem(const T *) itself need
not be templated.


void funcElem(const T * p) { ... } // not a template!

// only this is a template
template <class Container>
void func(const Container & c)
{
for_each(c.begin(), c.end(), funcElem);
}

And then:

vector<T *> vp;
vector<const T *> vcp;
// ...
func(vp); // OK
func(vcp); // OK


I appreciate the amount of work that you have put into gathering all C+
+ criticism in a single place, but I'm afraid you got into the trap of
being focused on problems instead of solutions.

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Eugene Gershnik

unread,
Oct 31, 2007, 7:35:59 PM10/31/07
to
Yossi Kreinin wrote:

> I learned that
> apparently D improved const.

How? If you know please post it here. If not this is a meaningless statement
in a technical forum.

> However, I don't think it's very important to get const right. The
> ultimately important thing is usability.

I don't know what "usability" is for you and how you define it. If you can
provide a good measurable definition please provide it.
Otherwise I can and do claim that const imporves 'usablity' as I understand
it.

What I do know is that a language should allow one to express design
concepts as clearly as possible. There is an exteremely important concept
like 'this thing is supposed to be logically unchanged' in any design. One
ought to be able to express it at the language level.

> From my experience, a
> language without const is sure usable.

For *your* definition of usable and from *your* experience.

> But having the C++ kind of
> const, which breaks when you have levels of indirection, is suboptimal
> in terms of usability - you never get the typing quite right, and the
> compiler keeps complaining.

Meaningless words again.
As a name for 'logcally immutable value' const always works right, including
when you have levels of indirection. Your expectations of what const ought
to do are based on confusion. When your expectations are not met you react
with blaming const rather than adjusting them.

To address your confusion about levels of indirection, in C++

struct Foo
{
Bar * pBar;
};

doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as people
coming from other languages often believe. What it really means is 'Foo
knows about Bar'. As such, immutability of Foo doesn't mean immutability of
Bar. If you want to express a different relationship between the two, which
will imply immutability propagation, raw pointer is a bad choice. It is
pretty trivial to create a smart pointer, let's call it owning_ptr that
enforces ownership and propagates immutability. If you do that you will map
your design concepts cleanly into the language rather than fight it.
If you insist to use pointers for a design concept that they don't model
well you will have to deal with maintaning ownership and const-correctness
yourself. And yes, as any manual work this might be hard. There might be
technical reasons to do this but in vast majority of cases this is
unnecessary. The fact that you complain about const pointers so much only
tells me that you use them inappropriately most of the time.

> And avoiding const will not work well,
> either (you won't be able to do things like func(ClassName(args)), for
> example,

Wrong. You can avoid const pretty easily but you must do exactly what Java
or C# do. Namely:
a) Use garbage collector
b) Use func(new ClassName(args))

Now you are free not to use const.

> and if you need libraries that use const, it will get worse):

Why worse? Do what you do every two lines in other languages: cast.

> One counter argument is that the C++ approach still allows for more
> compile time checking compared to having no const at all. I think that
> compile time checking is overrated in this case compared to the
> usability problems

The famous "usability" again.

>, including the clarity of error messages (consider
> the ones you get if you say "map::iterator p=m.begin()" when m is a
> const map).

Are you talking about the language or particular compilers? Incidentally
this confusion is all over your site.
Then why is that a problem of const? You should have complained: "error
messages produced by C++ compilers are often hard to understand". This would
be a true and valid criticism. AFAIK and FYI there are proposals before C++
committee to address this.

> There are things you'd obviously not try to represent in a
> static type system (such as subsets of natural numbers - "this
> argument must be an even number, this one must be a prime number, and
> this one must be a number representing a Turing machine that halts").
> I think that "mutability" gets close to the edge here (I'm sincerely
> not sure at which side of the edge it is);

This argument tells a lot about your skills as a designer. You might want to
ponder the reason why a widely respected book like "Effective Java" had
chosen to include advice about immutability instead of dealing with prime
numbers.

> As to the idea to convert void func(const std::vector<const T*>&) to
> template<class It> void func(It b, It e): this forces me to make func
> a template, which moves the source code to header files slowing down
> the build and further complicates the error messages, etc., opening a
> can of warms unrelated to this sub-thread. I'm aware of this option
> and find it completely unacceptable.

Sorry but this is rubbish. You have been given a good solution that allows
you to express your design clearly in C++ language. This solution also
parallels the best (AFAIK) solutions available in competing languages. Case
closed as far as const is concerned.

If you are dissatisfied with build speeds, quality of error messages etc.
feel free to complain about them. You will find much more sympathetic ears
for that.

--
Eugene

Yossi Kreinin

unread,
Nov 1, 2007, 2:36:59 PM11/1/07
to
On Oct 31, 3:58 am, LR <lr...@superlink.net> wrote:
> "How about trying another programming language?"
>
> Could this be what you're driving at? If so, what do you have in mind?
>

This is exactly what I'm driving at. The language choice depends on
your application domain, organizational constraints and personal
taste, and discussing this seems off-topic here. A partial list of
languages I refer to when I speak about useful things C++ doesn't have
(or problematic things other languages haven't) is here:

http://yosefk.com/c++fqa/disclaimers.html

One particular thing that AFAIK applies almost always: low-level work
is better done in C than C++. I speak from lots of experience (but it
doesn't matter - what matters is whether my points in the FQA convince
you or not). By "low-level", I mean "when your resources are really
scarce" (compared to the "size" of the problem).

Yossi Kreinin

unread,
Nov 1, 2007, 2:36:41 PM11/1/07
to
On Oct 29, 3:22 am, "Alf P. Steinbach" <al...@start.no> wrote:

> A Frequently Questioned Answers is a very good idea, and I commend the
> effort you've put into this.

Thanks!


> > One thing I'm definitely interested in is whether I got
> > any facts wrong, and if I did, which ones.
>
> Well, most of it. ;-)
>

Well, I meant "facts" :) My definition of "facts" in this context is
here (together with the first factual correction submitted by a
reader):

http://yosefk.com/c++fqa/web-vs-fqa.html

> One main high level issue that the FAQ gets wrong is the purpose of a
> programming language. In general it's a good idea to use the most
> appropriate language for a task, but your FQA seems to assume that
> somehow everything has to be done in C++.

The FQA doesn't say that everything has to be done in one language
anywhere, and I use several languages myself. The FQA says that /
nothing/ should be done in C++. This does not imply that "if C++ were
good, you'd have to do anything in it" or anything like that.

> Garbage collectors such as the Boehm collector work well.

One problem is that several libraries may be built to use different
garbage collectors. Garbage collectors are not entirely non-intrusive;
they may ask the user to provide some kind of layout information or
finalization functions, for example. Having many of them in one
program is thus problematic.

Non-intrusive garbage collectors have false positives (some things
look like pointers, but aren't), and fail when people use formally
illegal, but normally working techniques like int* one_based_vector =
(int*)malloc(sz)-1;

One thing C++ garbage collectors can't do is heap compaction. AFAIK
this increases the upper bound on the memory consumed by a program
with the peak sum of allocated object sizes equal to M from M to
M*log(max_block_size), which is a huge difference for realistic values
of max_block_size.

However, for
> many C++ programmers garbage collection is not desirable, because while
> it can ensure that memory is always reclaimed, it leads to other problems.

If you refer to /hard/ real time applications (most large ones are
really /soft/ real time), than AFAIK operator new has the same
problems in terms of worst case performance. If you refer to resource
leaks when resources!=memory, then I have to point out that the vast
majority of resources /is/ memory, and very basic language constructs
(such as new) depend on being able to acquire memory, and not having a
simple solution for memory management is likely the single biggest
problem with "low-level" languages when you try to do "high-level"
work in them (but C++ is promoted as a pretty high-level language).
IMO not acknowledging the difference between "memory" and "resource"
is a manifestation of perfectionism ("solutions that only solve 95% of
the problem are worthless"), and there's plenty of such things in C++.

>
> >From the FQA:
> > And what if you make a mistake, one single mistake with all these deletions?

> with proper RAII, if an object is still alive, then its class invariant
> still holds.

RAII does not at all prevent problems with dangling references. If you
keep a pointer into an object in an STL container, which acquires
memory in the constructor and deallocates its memory in the destructor
(which is what RAII means), and the container dies, you have a
dangling reference. Which is why C++ programs will frequently make
themselves private copies of objects - pointing to something you don't
"own" is dangerous.

You can implement reference counting on top of RAII, making all your
pointers "smart" (and having the usual trouble when integrating with
other libraries of equally smart though different, or, most often,
dumb bare pointers). Then you'll have the problem with cyclic
references (which, by the way, is AFAIK the single case when a GC
could leave "zombie objects" around - and note that it took us way
less work to get to this stage).

-- Yossi


--

Walter Bright

unread,
Nov 1, 2007, 2:47:37 PM11/1/07
to
Maciej Sobczak wrote:
> The clarity of error message has nothing to do with whether constness
> is a good idea or not. It's a quality of implementation issue. You can
> say that 100% of compilers are crappy when it comes to error messages
> (and you might be surprised how many of C++ programmers would actually
> agree), but that is still far from saying that the language itself
> sucks.

Pedantically, you're right. On the other hand, a lot of very smart
people have poured enormous resources into developing and refining C++
compilers. If 100% of the C++ compilers generate crappy error messages
(relative to error messages for compilers of other languages), then it
is a reasonable thing to ask what is it about C++ that makes it very
hard to do good error messages?

I.e. it *is* a problem with the language. The C++ committee recognizes
this, as concepts are an attempt to make it easier to generate better
error messages. How well this will turn out is too soon to know.

----
Walter Bright


Digital Mars C, C++, D programming language compilers

http://www.digitalmars.com

Walter Bright

unread,
Nov 1, 2007, 2:45:51 PM11/1/07
to
Eugene Gershnik wrote:
> Yossi Kreinin wrote:
>
>> I learned that
>> apparently D improved const.
>
> How? If you know please post it here. If not this is a meaningless statement
> in a technical forum.

It turns out that designing a const system in a language is a
surprisingly difficult problem. We worked for about 9 months to design
one for the D programming language, but it has turned out to be too
complicated, and is undergoing a redesign.

Setting aside the details of syntax, the two fundamental differences
between the D const system and the C++ one are:

1) D has two kinds of const: const and invariant. Const is, like C++, an
immutable view of data. Other views of that same data may be mutable.
Invariant represents data that is immutable. C++ does have invariant
when const is used as a storage class, but there's no way to declare a
pointer to immutable data in C++.

2) const (and invariant) in D is transitive. That means if you have a
pointer to const T, anything reachable through that pointer cannot be
mutated. This is different from C++, where although T is const, nothing
T refers to is necessarily const.


One controversial thing that D's const doesn't have is the 'mutable'
keyword which is there to support the notion of 'logical constness'. It
is a deliberate design choice to not support logical constness with the
const system.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Francis Glassborow

unread,
Nov 1, 2007, 5:24:49 PM11/1/07
to
Eugene Gershnik wrote:
>
> To address your confusion about levels of indirection, in C++
>
> struct Foo
> {
> Bar * pBar;
> };
>
> doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as people
> coming from other languages often believe. What it really means is 'Foo
> knows about Bar'. As such, immutability of Foo doesn't mean immutability of
> Bar.

Let me elaborate on the above:

struct Foo {
Bar * bar_ptr;
Foo(Bar * b_ptr): bar_ptr(b_ptr){}
};

Objects of type Foo are allowed to know about an object of type Bar (the
passed pointer might be a null pointer so they do not necessarily know
about a specific Bar. It is a little like having the key to a house, it
allows you access but does not guarantee ownership or responsibility for
the object.

struct Foo {
Bar * bar_ptr;
Foo(Bar * b_ptr): bar_ptr(new Bar){}
~Foo(){ delete bar_ptr;}
};

This is a strong 'ownership' relationship (better provided by a
smart-pointer) and each object of type Foo is responsible for the life
of an object of type Bar.

Note that the first of these is fragile because nothing has been done to
deal with the problem of a Bar object going away prematurely.

--

Eugene Gershnik

unread,
Nov 2, 2007, 2:31:23 AM11/2/07
to
Yossi Kreinin wrote:
> If you refer to resource
> leaks when resources!=memory, then I have to point out that the vast
> majority of resources /is/ memory,

[...]

> ("solutions that only solve 95% of
> the problem are worthless")

Yet another often repeated and unsubstantiated claim. How did you arrive at
the 95% figure? Hopefully not by counting 1MB memory allocation as
equivalent to 1 million mutexes.
In *my* experience the vast majority of resource types are not memory. The
raw non memory types include sockets, synchronization primitives, threads,
files etc. But this is only the beginning of the story. What "memory is the
only resource" people don't realize is that any time you have

struct Foo
{
NonMemoryResource bar;
};

Foo immediately becomes non memory resource too and has to be disposed of
correctly. And so on up to the top level owner. This is why 'database
connection' is a non memory resource even though it is a high level
abstraction. In C++ this comes for free but in your favorite GC-but-no-RAII
languages it is a manual task. Counting these "indirect resource" classes
the count of non-memory vs. memory resource becomes much bigger.
Incidentaly this is one of the largest sources of resource leaks in Java
code I have found. Even though a raw resource there may have a close()
function higher level classes that use it often forget to supply their own
close()-ing mechanism. Even when such close() is present, the user of the
higher level class often ignores to call it hoping for GC to do the job and
not realizing that there is e.g. socket inside.

There are some domains/problems where GC is really the best choice memory
management. An example I have personally met is implementing tree-like
object relationships. There were other examples pointed out by much smarter
people in lengthy discussions on this forum which you have obviously never
read.

There probably also exist domains where memory is, indeed, the main
resource. However, I doubt that many C++ programmers ever met them.

> and very basic language constructs
> (such as new) depend on being able to acquire memory, and not having a
> simple solution for memory management is likely the single biggest
> problem with "low-level" languages when you try to do "high-level"
> work in them (but C++ is promoted as a pretty high-level language).

FUD and demagogery.

This is IMO also a good description of your entire approach. You criticise
things without defining what you are criticising (const or compiler
messages?), constantly shift the targets of criticism (language, specific
compiler or a debugger?), make unsubstantiated claims ('often', 'biggest',
95%) and use meaningless terminology ('usability') in order to evoke
emotional reaction from the reader. In short you use debate 'techniques'
commonly used in political and religious arguments.
Sometimes, you come close to identifying some real problems in the language
but from what I have seen this is rather accidental.

--
Eugene

Nevin :-] Liber

unread,
Nov 2, 2007, 2:32:48 AM11/2/07
to
In article <1193873515.2...@57g2000hsv.googlegroups.com>,
Yossi Kreinin <yossi....@gmail.com> wrote:

> Well, I meant "facts" :) My definition of "facts" in this context is
> here (together with the first factual correction submitted by a
> reader):
>
> http://yosefk.com/c++fqa/web-vs-fqa.html

It is basically just a rant, not facts.

For instance, you claim that people don't measure performance of
templated code <http://yosefk.com/c++fqa/templates.html#fqa-35.10> and
then go on to claim that templated code in most cases is slower than non
templated code due to i-cache effects. Yet nowhere can I find any of
*your measurements* which show this to be true. Where are your
measurements?

Take a simple templated function which calls a templated algorithm:

template<typename T, ::size_t N>
void SortAnArray(T (&array)[N])
{
std::sort(array, array + N);
}

Now, pick any other language, and show us that this C++ version (with
optimizations turned on, of course, since inlining is an important part
of templated code performance) is slower for most invocations of this
with fundamental types (this way we leave out variations due to poorly
written operator<) and various sizes and initial randomness of the
array. Other than handcoded assembly by an expert, I seriously doubt
you will.

You make other wild claims, such as vector<int> and vector<void*> not
sharing code, yet I know of a least one C++ implementation (Metrowerks)
that has done so for years.

--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> 773 961-1620

Yossi Kreinin

unread,
Nov 2, 2007, 2:59:11 AM11/2/07
to
On Nov 1, 1:35 am, "Eugene Gershnik" <gersh...@gmail.com> wrote:
> Yossi Kreinin wrote:
> > I learned that
> > apparently D improved const.
>
> How? If you know please post it here. If not this is a meaningless statement
> in a technical forum.

I primarily referred to making const transitive (that is, if an object
is const, you can't modify anything via that object - all pointers
you'll get via it will be const, too); Walter Bright's message has the
full details.

I mentioned it because you talked about other languages, and didn't
elaborate on it since for me, personally, the ways of getting const
right are not the main theme of this discussion; the main theme is
what happens when you get const wrong in the language.

> I don't know what "usability" is for you and how you define it. If you can
> provide a good measurable definition please provide it.
> Otherwise I can and do claim that const imporves 'usablity' as I understand
> it.

In a complicated context like programming frequently all we can do is
point out that something exists ("const helps me since I don't have to
check whether that object is modified in the code"/"const gets in my
way because I can't get the typing right when there are levels of
indirection"). Reasonably quantifying the importance of such things,
however, is impossible since approximating the distribution of the
"relevant" programs and programmers is impossible.

What I claim is this. Most languages don't have const and most
programmers consider this tolerable. C++ does have const, which
according to most programmers helps in many simple cases. However,
when you have levels of indirection, you simply can't do "the right
thing", which can be objectively demonstrated, and programmers end up
copying objects or casting pointers or making everything template,
each which has it's own problems and is a way to work around the
problem, not solve it. I claim that the damage done by the complicated
cases exceeds the benefits gained in the simple cases.

Evaluating this claim against its exact opposite can't be done
objectively, so there's not much to argue about - different people
have different taste and experience. What can be discussed objectively
is the /existence/ of the problem in the cases when there's
indirection.

>
> What I do know is that a language should allow one to express design
> concepts as clearly as possible. There is an exteremely important concept
> like 'this thing is supposed to be logically unchanged' in any design. One
> ought to be able to express it at the language level.

...But you wouldn't quantify /that/ knowledge (by measuring the
benefits of "clear expression", for example), would you? :)

The problem with clearly expressing immutability in C++ is that it
isn't clear where "this thing" which shouldn't be changed ends (see
also below). I agree that in general, there's lots of potential value
in specifying constraints on values of variables. However, these
specifications can also cause problems, by not exactly matching the
constraints you'd really like to specify, and/or by simply cluttering
the code and/or producing compile time error messages where no run
time error would occur. So each special case of such things has to be
evaluated separately.

>
> > From my experience, a
> > language without const is sure usable.
>
> For *your* definition of usable and from *your* experience.
>

Well, quantifying is hard, but in this particular case, you have to
agree that lots of people using Java, C#, VB, Perl, Python, Ruby and
JavaScript (among lots of other languages) don't care much about not
having const. It's not just my experience in this case. Of course this
doesn't "prove" my claim, just shows that it isn't as exotic as you
make it sound.

>
> To address your confusion about levels of indirection, in C++
>
> struct Foo
> {
> Bar * pBar;
>
> };
>
> doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as people
> coming from other languages often believe. What it really means is 'Foo
> knows about Bar'. As such, immutability of Foo doesn't mean immutability of
> Bar.

I don't have any "confusion" about this, I know exactly what const
means for years, I don't "come from other languages", and please let's
not discuss each other and concentrate on C++, a subject we both know
something about.

Here's an example which I brought up earlier in the thread - a real
example of a simple attempt to use const and the standard library
given the C++ definition of const you've just correctly described:

http://yosefk.com/c++fqa/const.html#fqa-18.1

Why isn't this a problem? Why wouldn't having no const in the language
be better /in terms of clarity/? Why is /not knowing/ from the
function prototype that something isn't going to change so bad,
especially considering the fact that you /yourself/ give an advice to
cast const away in the body of the function?

Furthermore, why is it "good" that to implement an STL-like container,
you need to implement an iterator and a const_iterator? What happens
if you have a std::vector-like container that can get the storage
pointer from outside (as opposed to allocating it in the constructor),
and we want to attach a const storage pointer to it in some contexts
and non-const storage pointer in other contexts? Should we have a
vector and a const_vector? "const vector" wouldn't do just like "const
iterator" won't: with a const storage pointer, the vector /itself/ can
be modified (you can resize it, for example).

I would appreciate if you clarified your position on these particular
issues.

> If you want to express a different relationship between the two, which
> will imply immutability propagation, raw pointer is a bad choice. It is
> pretty trivial to create a smart pointer, let's call it owning_ptr that
> enforces ownership and propagates immutability. If you do that you will map
> your design concepts cleanly into the language rather than fight it.

Writing smart pointer classes to avoid the problems with const is what
I call fighting the language. You write lots of code ultimately doing
nothing. IMO, the readability gained from understanding which objects
can be modified by a piece of code is lost many times over by having
much more code.

> If you insist to use pointers for a design concept that they don't model
> well you will have to deal with maintaning ownership and const-correctness
> yourself. And yes, as any manual work this might be hard.

Writing smart pointer classes to deal with const correctness is also
manual work.

>
> > And avoiding const will not work well,
> > either (you won't be able to do things like func(ClassName(args)), for
> > example,
>
> Wrong. You can avoid const pretty easily but you must do exactly what Java
> or C# do. Namely:
> a) Use garbage collector
> b) Use func(new ClassName(args))
>
> Now you are free not to use const.

a) Garbage collectors don't work with C++ in practice, see another sub-
thread.
b) This has nothing to do with garbage collection. If C++ didn't have
const like most languages, and you had a void func(ClassName&), then
func(ClassName(args)) would work just like func(new ClassName(args)).

>
> > and if you need libraries that use const, it will get worse):
>
> Why worse? Do what you do every two lines in other languages: cast.

I don't do it in other languages, and that would be lots of casts.

>
> Are you talking about the language or particular compilers? Incidentally
> this confusion is all over your site.

This isn't "confusion", either. For someone actually writing software,
a language is only as good as the best implementations you can use in
practice. And C++ is old enough and popular enough to give people
enough time and motivation to create high quality implementations.
Whatever doesn't work smoothly by now has low chances to improve
significantly in the future.

>
> This argument tells a lot about your skills as a designer.

I could discuss the different notions of "design" different people
have, but I'd like to stick to the topic, which is const and neither
you nor me.

>
> Sorry but this is rubbish. You have been given a good solution that allows
> you to express your design clearly in C++ language. This solution also
> parallels the best (AFAIK) solutions available in competing languages. Case
> closed as far as const is concerned.
>

But it's open as far as template is concerned.

> If you are dissatisfied with build speeds, quality of error messages etc.
> feel free to complain about them. You will find much more sympathetic ears
> for that.
>

The problems with the build speeds of templates are inherent to the
design of this language feature:

http://yosefk.com/c++fqa/templates.html#fqa-35.12

I've spent lots and lots of time working with C++, and I understand
your position on this issue quite well. You value precise static
typing (which you call "clear expression") very high and the actual
problems of "implementations" very low, nor do you acknowledge that
these problems really come from the language definition. You're also
very tolerant to the need to write wrapper code, and prefer the
benefits of precise modeling to the drawback of having more code to
read and maintain.

There are different perspectives on these issues, and I think you
could agree that they are "internally consistent". But the main point
is the concrete examples, like the "const vector"/"const_vector"
issue: why do you think that the behavior of the language makes sense
with them?


--

Eugene Gershnik

unread,
Nov 2, 2007, 3:07:07 AM11/2/07
to
On Nov 1, 11:45 am, Walter Bright <wal...@digitalmars-nospamm.com>
wrote:

> 1) D has two kinds of const: const and invariant. Const is, like C++, an


> immutable view of data. Other views of that same data may be mutable.
> Invariant represents data that is immutable. C++ does have invariant
> when const is used as a storage class, but there's no way to declare a
> pointer to immutable data in C++.

Interesting. Is this distinction primarily to help optimizer or does
it have some high level design uses?

> 2) const (and invariant) in D is transitive. That means if you have a
> pointer to const T, anything reachable through that pointer cannot be
> mutated. This is different from C++, where although T is const, nothing
> T refers to is necessarily const.

Without knowledge of D it is hard to tell whether this would work. How
do you model
'immutable X that knows about mutable Y'?

> One controversial thing that D's const doesn't have is the 'mutable'
> keyword which is there to support the notion of 'logical constness'. It
> is a deliberate design choice to not support logical constness with the
> const system.

Hmmm. Rather than give the obligatory 'string that caches the length'
example let me give you something closer to what I usually see

class Mutex
{
void Lock(); //non const, changes the mutex
void Unlock(); //non const, changes the mutex
};

class SynchronizedInt
{
int value;
Mutex mutex;

int ReadValue() const //doesn't change value
{
Lock lock(mutex); //for this to work mutex must be mutable
return value;
}

};

How do you do it in D?

--
Eugene

Eugene Gershnik

unread,
Nov 2, 2007, 3:04:09 AM11/2/07
to
On Nov 1, 2:24 pm, Francis Glassborow
<francis.glassbo...@btinternet.com> wrote:

>
> struct Foo {
> Bar * bar_ptr;
> Foo(Bar * b_ptr): bar_ptr(new Bar){}
> ~Foo(){ delete bar_ptr;}
>
> };
>
> This is a strong 'ownership' relationship (better provided by a
> smart-pointer) and each object of type Foo is responsible for the life
> of an object of type Bar.

The important thing here is that 'ownership' concept is implicit in
such code. Nothing in the line

Bar * bar_ptr;

means ownership. It is just the same old 'knows about'. Only by
looking at what the implementation actually does in ctor, dtor etc.
you can discover that it uses 'knows about' to model 'owns'. This is
what I meant by manual (hard, error prone etc.) method of implementing
ownership.
An imaginary ideal language would provide 'ownership' as a language
level primitive. Something like

//pseudo language
class Foo
{
Bar * [owned, propagate immutability] bar_ptr;
};

In absence of such feature the best one can do is to write manual
ownership implementation once in a form of some 'smart' pointer and
never again have to deal with it. Thus you probably can get something
like

//C++
class Foo
{
smart_ptr<Bar, owning, propagate_immutability> bar_ptr;
};

using policy based designs. In practice a dedicated smart pointer
class with required semantics is usually enough.

--
Eugene

Yossi Kreinin

unread,
Nov 2, 2007, 3:12:28 AM11/2/07
to
On Nov 1, 1:36 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote:

> From some other exprience, a language without any static type system
>
> is also surely usable. Should we criticize those that have a static
> type system?

Well, if we should, that certainly isn't because the dynamically typed
ones are usable, at least not because of that by itself. I criticized C
++ const because it doesn't deal with the cases when a const object
itself points to other objects very well (I understand how the
behavior is defined, I just don't think the definition is useful in
practice). Based on /that/, I claim that having no const at all is
better than having C++ const.

>
> I have found recently (or better: I have came to appreciate) that
> constness is essential for code clarity - to the point that I would
> even endorse having const as a default (!) with some 'variable'
> modifier (or just 'mutable' as an existing keyword) or such to mark
> those objects that are actually allowed to change.
>
> Difference in expectations.

Not neccessarily :) I appreciate ways to specify constraints on the
program data (and control) flow. And I think const is useful in the
simpler cases, when the const object doesn't itself contain pointers
(especially to objects that themselves contain pointers, etc.). All I
am saying is that the confusion it causes with multiple levels of
indirection exceeds the benefits you get in the simple cases. This
can't be proven (it can only be demonstrated that the problem exists,
but its importance can't be quantified), nor does it mean that a
definition of const not entirely different from the one used in C++
can't be more useful than not having any kind of const according to my
own (informal) criteria.

>
> Mutability is one of the simplest things we can assert about any given
> object. Much simpler than, say, type substitution. Why not using the
> type system for such a simple thing?

I don't know how simple it is if we consider the issue theoretically,
from the perspective of the designer of a new language. I know that
the const definition in C++ has problems with indirection, and they
come from a basic question: where does an object end?

Consider std::vector. There are two kinds of modifications: changes to
the elements (like v[i]=x) and changes to the vector itself (like
v[i].reserve(n)); some changes fall into both categories (like
v.clear()). Now, suppose we want to have a vector-like class that
works with storage buffers passed as constructor arguments, sometimes
the buffers are const and sometimes they aren't. Clearly a vector that
was passed a const storage buffer can still be changed (for example,
we could attach a different storage buffer to it), without modifying
the buffer. So const vector is not the right way to represent a vector
working with a const buffer. What should we do - implement a
const_vector (the way we have const_iterator, for these same reasons)?
See also the adjacent sub-thread.

> It might only contain a loop that calls void funcElem(const T *) for
> each iterator value in the range, and funcElem(const T *) itself need
> not be templated.
>
> void funcElem(const T * p) { ... } // not a template!
>
> // only this is a template
> template <class Container>
> void func(const Container & c)
> {
> for_each(c.begin(), c.end(), funcElem);
>
> }
>
> And then:
>
> vector<T *> vp;
> vector<const T *> vcp;
> // ...
> func(vp); // OK
> func(vcp); // OK

I think that having to write all that code in order to solve the
problems with const correctness illustrates the fact that we have a
problem. The ultimate point of const is clarity; the extra layers of
code seem to defeat the purpose (you do get the extra benefit of
better compile time checking; I've never seen any ROI from this in the
case of const though - people rarely end up modifying random things,
the benefit of const is primarily to tell a reader of the code what
people most certainly didn't want to modify in the first place).

>
> I appreciate the amount of work that you have put into gathering all C+
> + criticism in a single place, but I'm afraid you got into the trap of
> being focused on problems instead of solutions.
>

Thank you, and I don't think it's a trap - some problems are best
solved by being avoided, and you have to "focus" on the problems to
see if you want them solved locally or by avoiding the whole bunch of
them (in this case, by programming language choice).

By the way, I have lots of C++ programming experience, in particular
experience with finding solutions like these. In particular, whenever
you have a typing problem (you don't want to spell out a type name or
you have many types the language considers "unrelated", like
vector<const T*> and vector<T*>), one thing you can do is add a
template layer and solve the problem. That complicates the control
flow though, and creates lots of code to read and write.

Hyman Rosen

unread,
Nov 2, 2007, 12:34:23 PM11/2/07
to
Yossi Kreinin wrote:
> The problems with the build speeds of templates are inherent to the
> design of this language feature

I work with a project that contains many hundreds of C++ files,
pretty much all of which use class and function templates from
the standard library and our own. I can do a clean recompile of
the whole system in ten or fifteen minutes or so on my Solaris
x86 machine using the Sun CC 11 compiler. For normal work, the
source compilation time is inconsequential - it's linking that
takes most of the time, and even that isn't too long.

Hyman Rosen

unread,
Nov 2, 2007, 12:35:14 PM11/2/07
to
Yossi Kreinin wrote:
>> vector<T *> vp;
>> vector<const T *> vcp;

You know, you keep carping about how these two types are
incompatible, as if it were a given that this should not
be so. But in fact, the two classes can be very different,
because C++ allows specialization. It's not the case for
std::vector, but it can be the case for other classes.

As a simple example, I can create

template<typename T> class p;
template<typename T> class p<T const *> { T const &deref_const(); };
template<typename T> class p<T *> { T &deref(); };

How in the world should the compiler allow passing a
p<char *> to a function expecting a p<char const *>?
They don't even have the same methods. Templates with
different parameters are entirely different types, and
there should be no expectation of substituting one when
another is called for. You could argue with as much
futility that it should be permitted to pass an X<char>
to a function expecting an X<long>, since after all a
long can hold every value in a char's range.

khall....@gmail.com

unread,
Nov 2, 2007, 12:36:27 PM11/2/07
to
On Oct 26, 1:27 am, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.

Secion "No compile time encapsulation"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Recompilation can be avoided by using the PIMPL pattern:
http://en.wikipedia.org/wiki/Opaque_pointer .
This problem is not unique to C++; C has similar problems.


Section "Defective operator overloading"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

First off, there is no restriction that says operators must return by
value. The << operator overloaded for std::ostream is a perfect
example. The normal and recommended way of overloading << is by
returning a reference, not a value:

std::ostream& operator<< (std::ostream&, {some type here});

Also, smart pointers can be used when needing to return "new'd"
pointers. There's one in the current standard (std::auto_ptr) and a
few more that will be in the next standard.


Section "Defective exceptions"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The RAII idiom works very well. I've was the project lead on a C++
project that involved 200,000 lines of code and there were only 2
instances of the "new" keyword. (And yes, we did use exceptions, OOP
conecpts, the STL, as well as other paradigms and features of C++.)
By using standard containers, smart resources (for non-pointer items
like mutex lockers), etc..., large software projects can be built that
properly release memory and resources when exceptions occur.

And by the way, the RAII idiom isn't what is wrong with C++
exceptions. Similar problems exist in other languages that have
exceptions too. How does one release a resource in C# when an
exception occurs? Via a 'finally' statement or a 'using' statement.
The problem is that one must be careful when writing code that uses
exceptions. (I for one prefer RAII to 'finally' and 'using'
statments, but that's a personal preference, not a rule.)


Section "Duplicate facilities"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can find "devotees" to rattle off all sorts of nonsense for any
argument under the sun. But that doesn't mean that the experts share
that view -- or even that the common user shares that view. No
language feature is 'evil' (the intent with which it is used may be --
see obfuscated C contest ;] ).

Note also that the reason std::string and std::vector exist is because
the C facilities have their shortcomings: namely the inability to
resize themselves. You're right, the std::string and std::vector are
in some ways inferior to char* and arrays, but in some ways they are
superior too. There's a tradeoff that must be considered.


Section "No high-level built-in types"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You said "C++ doesn't add any built-in types to C."
What about 'bool'?
Also, depending on your definition of "built in", you could include
all the standard library classes, which actually is quite a large
number (std::string, std::ifstream, std::ostream, etc...)


Section "Defective metaprogramming facilities"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Macros are not lame at all. They are useful when created domain
specific languages (http://en.wikipedia.org/wiki/
Domain_Specific_Language) as well as helping out sometimes with other
tasks. Sure, they have been mis-used but that's not their fault.
They are incredibly helpful when used properly.

By the way, macros and templates make C and C++ two of the few
languages to be able to create DSLs that are parsed by the compilers
themselves rather than having to build a program or library to parse
DSL. That's nifty (and useful)!


I could continue on, but I need to go to bed now as I'm falling asleep
typing.

Jerry Coffin

unread,
Nov 2, 2007, 12:33:52 PM11/2/07
to
In article <mr-dndWKz9q5sbTa...@comcast.com>,
wal...@digitalmars-nospamm.com says...

[ ... ]

> I.e. it *is* a problem with the language. The C++ committee recognizes
> this, as concepts are an attempt to make it easier to generate better
> error messages. How well this will turn out is too soon to know.

Yes and no. It's true that it's technically too soon to know about
concepts as part of the official C++ language, because (of course) they
won't be for a while. OTOH, concepts have been implemented in conceptgcc
so it's entirely reasonable for just about anybody who cares to download
a copy and start testing anytime they want to.

My own experience is pretty simple: using conceptgcc, virtually all of
the hard to read error messages simply disappear, at least for code that
actually uses concepts. Of course, nothing forces you to do that, so if
you want to write code that generates arbitrarily lousy error messages,
you can still do so.

--
Later,
Jerry.

The universe is a figment of its own imagination.

Yossi Kreinin

unread,
Nov 2, 2007, 12:42:09 PM11/2/07
to
On Oct 28, 8:01 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
>
> Although it seems you did a fair amount of research, to me your post
> is not much more than a very sophisticated way of saying "C++ SUCKS"
> on a moderated C++ newsgroup. :-)

Most of the FQA is not based on "research" done for the purpose of
writing it, but on my experience using C++ and watching others do it.

My purpose is to convince people, not to irritate the ones I can't
convince; I see no practical way to attempt to do the former while
completely avoiding the latter though.

>
> "No compile time encapsulation"
>
> This item implies that you have to ship the implementation class
> declaration to clients, which is not necessarily true. Have you ever
> heard about abstract classes ("interfaces")?

I know the ways to work around the problems with private:

http://yosefk.com/c++fqa/inheritance-abstract.html#fqa-22.1 (abstract
classes)
http://yosefk.com/c++fqa/heap.html#fqa-16.21 (pimpl)
http://yosefk.com/c++fqa/class.html#fqa-7.5 (incomplete types)

The C++ solutions (the first two) require you to write wrapper code,
the C solution (the last one) does not, which IMO makes it the best
(I'm referring to the context where you'd use private but you want to
avoid recompilation upon changes to private members and this is the /
only/ problem you're solving; abstract classes have other merits, and
the FQA acknowledges them).

However, workarounds aside, C++ does have private, and there's a
separate question of whether it's useful or not. I claim that the
advantages (getting a compile time error when someone accesses a
member you didn't want them to) are dwarfed by the drawbacks. Most C++
classes are neither pimpls nor implementations of base classes (that
takes manually written wrapper/bridge code) and of course not C-like
incomplete types (naturally, people most often use C++ classes when
they program in C++). Changes done to the private members of those
classes are responsible for a significant amount of time spent for
rebuilding C++ code (of course I don't have numeric data proving this,
and you don't have numeric data refuting this; all we can do is to
point out the things that happen, not to measure their importance).

>
> "Outstandingly complicated grammar"
>
> Maybe, you have your point here.

Note that frequent rebuilds would be less of a problem if C++ compiled
fast, and the fact that it follows from the language definition, not
the "low quality" of all implementations available today:

http://yosefk.com/c++fqa/defective.html#defect-2
http://yosefk.com/c++fqa/defective.html#defect-3
http://yosefk.com/c++fqa/defective.html#defect-12
http://yosefk.com/c++fqa/defective.html#defect-14
http://yosefk.com/c++fqa/defective.html#defect-16

>
> "No run time encapsulation"
>

I'm not claiming that every language should have run time
encapsulation. Frequently, language features can not be judged by
themselves, but only if you consider how they interact.

The problem with having no run time encapsulation /in C++/ is that the
language has no reflection and several different and non-trivial
commercially significant ABIs. This means, for example, that when a
program crashes, figuring out what's going on in the core dump is hard
compared to, say, a program compiled from C source (C doesn't have
reflection, either, but maps more straight-forwardly to executable
code):

http://yosefk.com/c++fqa/defective.html#defect-4

>
> "No binary implementation rules"
>
> Frankly, I don't see your point here. You have tried to link g++-
> compiled libraries with msvc-compiled code, or what exactly?
>

I didn't, because I know it doesn't work. I wish it did though. Are
you saying that this is a non-problem, and if so, why?

> "Very complicated type system"
>
>
> You can rewrite your function into a function template
> which will accept a range instead of `std::vector< whatever >&':
>
> template< typename Iter > void func2(Iter begin, Iter end);
>
> Of course, you loose that prominent `const*' in declaration, but as
> long as implementation complies with the given pact (not to modify
> whatever iterators "point to"), I think it's pretty OK.

I disagree, because of the many problems with templates:

http://yosefk.com/c++fqa/templates.html#fqa-35.1

I'm not shifting the focus of the discussion, just saying that solving
the problem with const by introducing much more complicated problems
related to templates has drawbacks.

> Anyway, I don't recall myself applying cv-qualifiers to the type of
> contained elements in a sensible and useful way. Anyone?

What about when you fill your vector with objects obtained by calling
a method returning a const reference in a loop?

> > ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.
>
> Maybe, if you prefer to write everything from scratch instead of
> trying to stick to standard library.

What about when you need a subset of objects kept in std::vector or
std::list, and the owning collection is about to be deallocated,
because you only need some of the objects, but not all of them
anymore?

http://yosefk.com/c++fqa/dtor.html#fqa-11.1

Do you copy the objects, which has a performance penalty, or keep
collections of pointers, so you have no RAII coming from the standard
library to help you with exception safety anymore, or do you use smart
pointer classes which do reference counting, which breaks when you
have cyclic references?


--

James Hopkin

unread,
Nov 2, 2007, 6:53:21 PM11/2/07
to
On Nov 2, 4:42 pm, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
>
> What about when you need a subset of objects kept in std::vector or
> std::list, and the owning collection is about to be deallocated,
> because you only need some of the objects, but not all of them
> anymore?
>
> http://yosefk.com/c++fqa/dtor.html#fqa-11.1
>
> Do you copy the objects, which has a performance penalty, or keep
> collections of pointers, so you have no RAII coming from the standard
> library to help you with exception safety anymore, or do you use smart
> pointer classes which do reference counting, which breaks when you
> have cyclic references?
>

With std::list you can use splice. As for contiguous containers
(vector, deque) - that's the tradeoff you make for the advantages of
contiguous allocation. None of the other popular languages give you
this ability (or only for a very restricted set of types like C# value
types).

Note that C++0x will give you the option of, for example,
std::vector<unique_ptr<T>>


James

khall....@gmail.com

unread,
Nov 2, 2007, 6:58:40 PM11/2/07
to
On Oct 26, 1:27 am, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> The C++ FQA Lite is available here:http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ -http://parashift.com/c++-faq-lite
>
> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got

> any facts wrong, and if I did, which ones.

[6.1] "Is C++ a practical language?"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You say "C++ is not 'well-supported' in the sense that development
tools for C++ lack features and are unreliable compared to other
languages."

I strongly disagree with that. All you need to do is go to
SourceForge.Net and go to "software development"->"Debugggers" and see
how many items are related to C++. Visual Studio 6.0 to Visual Studio
2008 offer incredible capabilities in debugging, with VS2008 showing
many very cool new features. There are many non-MS and *nix tools
available too: CodeBlocks, KDevelop, gcc, BoundsChecker, Electric
Fence, and the list goes on and on and on....

You also say "C++ is not "mature" in the sense that different
compilers will interpret it differently". While the very advanced
parts of the language may act different on some compilers, most behave
similarly for parts of the language that produce defined behavior.
Most programs that do not include undefined behavior will produce the
same results on the popular compilers.


[6.2] "Is C++ a perfect language?"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

All I can say is that you are ranting here. There are very few facts
here.

No language is "perfect" -- each has its pros and cons. C++ is a
practical language; it may not be practical for a specific
application, but it certainly is practical for many. Why? It
availability on almost every OS (Python is a very popular scripting
language, but it isn't available on VxWorks or uItron), C++ is a multi-
paradigm language (it's OK to mix OOP, procedural programming, meta-
programming, and templates), C++ is powerful (boost::spirit and
boost::mpl are amazing examples), and there are many tools out there
designed for C++.


[6.3] What's the big deal with OO?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You go far beyond the question to rant on C++. There is nothing wrong
with the FAQ's answer. It's brief, to the point, and answers the
question. You go off making assertions that are false (as I mentioned
earlier, use opaque pointers).


[6.5] Is C++ better than Ada? (or Visual Basic, C, FORTRAN, Pascal,
Smalltalk, or any other language?)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Your first paragraph is a perfect complement to the FAQ's answer. The
rest is a rant on C++. That other information, toned-down, could be
useful advice about how to interface C++ with other languages.


[6.6] Who uses C++?
~~~~~~~~~~~~~~~~~~~

Another rant. Please, explain what is factually incorrect with the
FAQ's answer?


[6.7] How long does it take to learn OO/C++?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you removed your anti-C++ bias, you would have a good complement to
the FAQ's answer. Yes, it takes a long time to master the language.
You mention some specifics, fine. But the FAQ's answer is correct
too.


[6.8] What are some features of C++ from a business perspective?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You state "No practical implementation of C++ runs in managed
environments, increasing both the defect rate and the potential damage
of an undetected defect".

And what does a managed environment buy, do tell? If a program is ill
formed in a managed environment, the environment may tell you more
information when the programs behaves incorrectly, but the program
still behaves incorrectly. The managed environment may have garbage
collection, but there are garbage collectors available for C++ too. C+
+ is a complicated language, and *that* can increase the defect rate.

And, by the way, C++/CLI does run in managed environments. While
techinically a different language than C++, it combines the most of
the powerful features of C++ with those of .NET.

You also state "Providing C++ interfaces to a software component is
impossible in practice due to lack of compile time and run time
interoperability". That's just simply wrong! My proof is the vast
number of sucessful C++ projects. What's your proof?

You state "C++ is extremely inconsistent and complicated, increasing
learning curves and the defect rate". Leave out the 'inconsistent'
part.

You state "C++ compilers typically fail to comply to its intricate
standard, reducing portability". While technically true, this is
practically false. Visual Studio, gcc, Digital Mars, Comeau, and
other popular compilers are all at least 98% conformant, with some
even reaching 100% conformance. The main feature missing from most
compilers is 'export'. Other than that, there are a few advanced
features that may not be perfectly portable. But most programs that
don't use compiler-specific extensions are very easy to port from
compiler to compiler.

You state "C++ compilation is both very slow and very frequent,
increasing development time and defect rate". This is not true. Good
design practices (one being opaque pointers), pre-compiled headers,
multi-processor and distributed compilation all help reduce long
compile times. It's also possible to edit a file while something else
is compiling. Practically speaking, a developer's job included many
other things than just editing code; if there is a particularly long
compilation occurring, it's an opportunity to write documentation,
write tests, go to a meeting, .... There's always something that can
be done.

You state "C++ lacks standard types representing basic data structures
like strings, arrays and lists (or has more than one standard and many
non-standard ones, which is the same)." That's just false. There are
standard types for each structure you mention. One may *think* that
there are more than one, but disregarding template parameters, that's
not technically true. There's only one doubly-linked list type, only
one resizable array with O(1) random access, only one double-ended
queue, etc.... All of these could be considered "lists" from a high-
level view, but each has very different performance characteristics
and is why people choose a particular type for a given job.

You state "C++ doesn't reduce safety-vs-anything trade-off since it's
extremely unsafe". This isn't true. The 'const' keyword (which has
been discussed may times already in this thread) has been a lifesaver
in my company. Our customers were confused why data was getting
modified when we stated that it shouldn't. Well, we didn't have
'const' in our interface. So we inserted it in, compiled and found
all the places we needed to carry it further. Following this process
quickly lead us to the bug. Now we use 'const' as a means of
enforcing our contract, and consequently we find these problems during
compilation time long before the customer even sees the product. My
biggest complaint about most recent languages is their lack of
'const'. I'm glad D didn't sacrafice this!

[6.9] Are virtual functions (dynamic binding) central to OO/C++?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

C++ is in some ways more object-orientated than more recent languages
like Java and C#. Neither Java nor C# supports multiple inheritance,
but that is one major feature of a true object-orientated language.
(And for the record, both Java and C# have many procedural methods
using "objects" more as a namespace than really an object. One cannot
create a System.Math object, nor is a System.Math object needed to
call System.Math.Cos).

[6.10]-[6.12]
~~~~~~~~~~~~~

These all look to be nothing more than rants. If you toned down your
language and presented a more objective view, you might have something
useful. As it stands, only people who already share an anti-C++ view
will support. As such, it's not going to help anyone.


[6.14]
~~~~~~

You state "Asking about things specific to C++ is not very useful."
That's not true at all. You acknowledge earlier in the FAQ that it
takes a long time to become proficient in C++. If a company already
has a C++ project that must be maintained, then simply grabbing a
talented person who isn't familliar with C++ is a poor buisness
decision. Sure, that person should be able to pick up C++ (since he/
she is talented), however comapny may not be able to afford the time
needed for the developer to gain that proficiency. If a C++ person is
needed, then asking C++ questions is legitimate!

[6.15] What does the FAQ mean by "such and such is evil"?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

First, you can't really tell us what someone else means. That's like
me telling you that what you really mean by your FQA is that "C++
sucks", despite your disclaimers to the contrary. This sounds just
like another rant.

While I personally hate that the FAQ uses the term 'evil', I
understand that the FAQ's author wants to bring attention to a topic.
The FAQ afterall is not some hallowed document -- it's just one man's
opinion. Other people will agree with the FAQ to varying degrees. I
agree with most, but certainly not all of it.

khall....@gmail.com

unread,
Nov 2, 2007, 6:52:07 PM11/2/07
to
On Nov 2, 9:42 am, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> On Oct 28, 8:01 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
> > Although it seems you did a fair amount of research, to me your post
> > is not much more than a very sophisticated way of saying "C++ SUCKS"
> > on a moderated C++ newsgroup. :-)
>
> Most of the FQA is not based on "research" done for the purpose of
> writing it, but on my experience using C++ and watching others do it.
>
> My purpose is to convince people, not to irritate the ones I can't
> convince; I see no practical way to attempt to do the former while
> completely avoiding the latter though.

I think you'll do a poor job of convincing people. The reason I
belive this is because you very frequently use an inflamatory tone in
your answers while not offering good alternatives. And the implied
alternatives frequently contradict one another. You argue that C++
exceptions are bad, but C++ exceptions are not really that different
from other language's exceptions (especially when compared against
your criteria for exceptions being bad). On the other hand, you argue
that C++ being an unmanaged and non-garbage collected language is
bad. Well, most mangaged, garbage collected languages out there use
exceptions.

It would be better to have a more objective tone. For example in the
"No compile time encapsulation" section, you could write: The nature
of C++ can make recompilation longer than it otherwise needs to be.
<insert your explanation of changing private members here and its
effect on compilation times>. Languages such as C, Ada, and D
(Walter correct me if I'm wrong) suffer similar problems and share the
same well-developed workaround: the "opaque pointer" (also commonly
referred to as the RAII idiom). The downside of using opaque pointers
is that is requires an extra level of abstraction and requires a bit
more time designing. Languages that have helped solve these problems
include: .... <list languages here> ...

If you answered each question in this manner, most C++ developers
would probably wouldn't have much of a problem with it. Sure, there
will always be die-hards, but you would not quite have the backlash
you currently have.


And here's something to consider: in my company (which uses primarily
C, but also C++, Python, C#, VB.NET, and a variety of other lesser
known langugages), I'm one of the few C++ advocates (though many
project are developed using C++). People tell me C++ is a complicated
language; the syntax is difficult; it's so prone to memory and
resource leaks. However, they are amazed when they see my C++
programs. They compliment me on how simple the program is (frequently
1/3 the size of a similar C program -- sometimes even much smaller;
often smaller and more concise than .NET programs too), how well it
runs, how bug free it is. I'm not trying to toot my own horn, rather
I'm trying to point out that C++ has the tools to make understandable,
robust programs. It's a challenging language to master, but when used
properly is a beautiful tool!

Walter Bright

unread,
Nov 2, 2007, 7:03:17 PM11/2/07
to
Hyman Rosen wrote:
> Yossi Kreinin wrote:
>> The problems with the build speeds of templates are inherent to the
>> design of this language feature
>
> I work with a project that contains many hundreds of C++ files,
> pretty much all of which use class and function templates from
> the standard library and our own. I can do a clean recompile of
> the whole system in ten or fifteen minutes or so on my Solaris
> x86 machine using the Sun CC 11 compiler. For normal work, the
> source compilation time is inconsequential - it's linking that
> takes most of the time, and even that isn't too long.

I've heard many reports of heavily templated C++ code having build times
measured in hours - such as overnight builds. Even so, a build time of
ten minutes is very consequential. Having a build time of a few seconds
can completely transform how one develops code.

I know very well how and why templates slow down compilation, and this
is inherent to how C++ templates work. It is not fixable.

There are other ways to do it, however. Templates are routinely abused
for things they were never meant for, because fundamental language
facilities like function literals are missing from the core language.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 2, 2007, 7:02:51 PM11/2/07
to
Jerry Coffin wrote:
> In article <mr-dndWKz9q5sbTa...@comcast.com>,
> wal...@digitalmars-nospamm.com says...
>
> [ ... ]
>
>> I.e. it *is* a problem with the language. The C++ committee recognizes
>> this, as concepts are an attempt to make it easier to generate better
>> error messages. How well this will turn out is too soon to know.
>
> Yes and no. It's true that it's technically too soon to know about
> concepts as part of the official C++ language, because (of course) they
> won't be for a while. OTOH, concepts have been implemented in conceptgcc
> so it's entirely reasonable for just about anybody who cares to download
> a copy and start testing anytime they want to.
>
> My own experience is pretty simple: using conceptgcc, virtually all of
> the hard to read error messages simply disappear, at least for code that
> actually uses concepts. Of course, nothing forces you to do that, so if
> you want to write code that generates arbitrarily lousy error messages,
> you can still do so.

I think it's very good that some of these new features are being tried
out in real implementations before finalizing them in the standard.
Frankly, I think *all* of the proposed new features should be tried out
in a real compiler before voting them in the standard. This would help
avoid disasters like the C++98 export. Even simple things should be
tried out, as it was discovered that the 'auto' feature was
unimplementable as specified.

Although conceptgcc does exist and that is great, realistically, a
complex feature like that needs to be in real use for years by a lot of
people before it becomes clear whether or not it solves the problem. For
example, namespaces were implemented and in wide use for years before
people realized that they didn't really work.

See the infamous "so sue me":
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/d9eb5db7019673e0/1aaae5a80f53bd4f?hl=en&lnk=st&q=namespaces+scott+meyers+%22so+sue+me%22#1aaae5a80f53bd4f

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 2, 2007, 7:03:01 PM11/2/07
to
Eugene Gershnik wrote:
> On Nov 1, 11:45 am, Walter Bright <wal...@digitalmars-nospamm.com>
> wrote:
>
>> 1) D has two kinds of const: const and invariant. Const is, like C++, an
>> immutable view of data. Other views of that same data may be mutable.
>> Invariant represents data that is immutable. C++ does have invariant
>> when const is used as a storage class, but there's no way to declare a
>> pointer to immutable data in C++.
>
> Interesting. Is this distinction primarily to help optimizer or does
> it have some high level design uses?

Invariant references, unlike const, can be used by the optimizer. The
invariant values can be cached, they are thread safe, they won't be
compromised by exceptions, and there's no need to worry about aliases to
them. Invariant references behave a lot like value types. This works out
particularly well for strings, as people intuitively think about strings
as if they were value types. Language after language (Java, Perl, etc.)
successfully treat strings as invariant without user problems. Strings
as mutable types don't work so well (I often run into code where people
defensively dup strings "just to be sure" someone else doesn't change them).

There's no way to have invariant strings with C++ const. This is often
dealt with by adopting a convention that strings are invariant, but
conventions are not reliable, and widely adopted conventions are ripe
for consideration for adopting into the language.

The sole reason to have const is so that the same function can deal with
both invariant and mutable types, as invariant and immutable can both be
implicitly converted to const.

The other advantages of invariant is it opens the door to being able to
prove things about functions, something that has been very elusive for
C++. It also opens the door to functional programming, a paradigm that
is not available for C++.


>> 2) const (and invariant) in D is transitive. That means if you have a
>> pointer to const T, anything reachable through that pointer cannot be
>> mutated. This is different from C++, where although T is const, nothing
>> T refers to is necessarily const.
>
> Without knowledge of D it is hard to tell whether this would work. How
> do you model
> 'immutable X that knows about mutable Y'?

The const X would have read only access to mutable Y. There is no way to
modify Y through const X. If you want to mutate Y through X, then X must
not be const.

This seems at first blush like "no way, this is a crippling
shortcoming." But is it? All I can say is look at real code, and look
for legitimate use cases of modifying a referenced object through a
const reference.


>> One controversial thing that D's const doesn't have is the 'mutable'
>> keyword which is there to support the notion of 'logical constness'. It
>> is a deliberate design choice to not support logical constness with the
>> const system.
>
> Hmmm. Rather than give the obligatory 'string that caches the length'
> example let me give you something closer to what I usually see
>
> class Mutex
> {
> void Lock(); //non const, changes the mutex
> void Unlock(); //non const, changes the mutex
> };
>
> class SynchronizedInt
> {
> int value;
> Mutex mutex;
>
> int ReadValue() const //doesn't change value
> {
> Lock lock(mutex); //for this to work mutex must be mutable
> return value;
> }
>
> };
>
> How do you do it in D?

You drop the 'const' from ReadValue(), because it is mutating the
instance of SynchronizedInt. Essentially, D doesn't support the notion
of an object pretending to be const, only objects that actually are. The
idea is that if you see a function signature that says its taking a
const argument, it really truly cross-my-heart promises that it *will
not* be mutating it.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Eugene Gershnik

unread,
Nov 2, 2007, 7:01:43 PM11/2/07
to
Yossi Kreinin wrote:
> On Nov 1, 1:35 am, "Eugene Gershnik" <gersh...@gmail.com> wrote:
>> Yossi Kreinin wrote:

> [...] for me, personally, the ways of getting const


> right are not the main theme of this discussion; the main theme is
> what happens when you get const wrong in the language.

Fair enough but you still didn't demonstrate that the const is done
wrong in
C++.

>> I don't know what "usability" is for you and how you define it. If
>> you can provide a good measurable definition please provide it.
>> Otherwise I can and do claim that const imporves 'usablity' as I
>> understand it.
>

[...]

> Reasonably quantifying the importance of such things,
> however, is impossible since approximating the distribution of the
> "relevant" programs and programmers is impossible.

So you agree that usability is in the eyes of the beholder. Good. In this
case can we drop this term from the discussion?

> What I claim is this. Most languages don't have const and most
> programmers consider this tolerable.

And what is the technical value of this argument? How is what most
people do
(they write in-house unmaintainable, code in VB actually) relevant to
anything. This is a common fallacy called, I believe, "Appeal To Widespread
Belief".

> However,
> when you have levels of indirection, you simply can't do "the right
> thing", which can be objectively demonstrated,

You have demonstrated nothing of the sort so far. Repeating an assertion
doesn't make it true.

>> What I do know is that a language should allow one to express design
>> concepts as clearly as possible. There is an exteremely important
>> concept like 'this thing is supposed to be logically unchanged' in
>> any design. One ought to be able to express it at the language level.
>
> ...But you wouldn't quantify /that/ knowledge (by measuring the
> benefits of "clear expression", for example), would you? :)

Sure I would. I am paid N dollars for every minute of work. Therefore every
minute I spend trying to figure out non-obvious things, typing code that I
shouldn't have, verifying things that ought to be known etc. costs my
employer N dollars.
If the code requires lots of external documentation (not the problem domain
one but the kind that explains what code is doing) or if the meaning of
expressions can only be figured from their implemenation and usage then
this
is going to be money wasted. Clear code, that is the code which is easy to
understand simply costs less money.
Note that this concept is only a part of a larger concept of
maintainability. The full maintainability also deals with thing like how
expensive is any given change to the entire system which are outside of the
language issues.
Based with this principle every language feature and every approach to
writing code can be analyzed to see how much money it wastes.

> However, these
> specifications can also cause problems, by not exactly matching the
> constraints you'd really like to specify,

An example please?

> and/or by simply cluttering
> the code and/or producing compile time error messages where no run
> time error would occur.

This is backwards. You seem to think that runtime error is the only error
worth worrying about. Almost every time you get a "const conflict" in your
code where you absolutely must cast it reveals a design problem. Which is
far more important in the long run than a runtime failure.

>>> From my experience, a
>>> language without const is sure usable.
>>
>> For *your* definition of usable and from *your* experience.
>
> Well, quantifying is hard, but in this particular case, you have to
> agree that lots of people using Java, C#, VB, Perl, Python, Ruby and
> JavaScript (among lots of other languages) don't care much about not
> having const.

"Appeal To Widespread Belief" again.

>> To address your confusion about levels of indirection, in C++
>>
>> struct Foo
>> {
>> Bar * pBar;
>>
>> };
>>
>> doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as
>> people coming from other languages often believe. What it really
>> means is 'Foo knows about Bar'. As such, immutability of Foo doesn't
>> mean immutability of Bar.
>
> I don't have any "confusion" about this, I know exactly what const
> means for years,

You missed the point entirely. The above wasn't about const. It was about
your famous levels of indirection. In short: if you use raw pointers to
model relationships other than 'knows about' you will have to do lots of
manual work which includes manually adjusting const-ness. And yes it is a
language deficiency that we don't have other kinds of pointers. Thankfully
this can be addressed almost completely by creating appropriate smart
pointers.

> Here's an example which I brought up earlier in the thread - a real
> example of a simple attempt to use const and the standard library
> given the C++ definition of const you've just correctly described:
>
> http://yosefk.com/c++fqa/const.html#fqa-18.1
>
> Why isn't this a problem?

Because of a fundamental principle that if Foo IS-A Bar then a
Collection<Foo> *not* IS-A Collection<Bar>. In other words your expectation
that this "ought to work" is similar to a beginner expectation that you can
pass an Array<Apple> to a function that expects an Array<Fruit>.
You can argue with this princple but it is a widely followed one (see I can
use "Appeal To Widespread Belief" too).
The solution is simply to have a function that takes an Array<Anything> or
(in a better language) an Array<Anything which IS-A Fruit>. This is exactly
what you have been offered here and if it isn't good enough for you there
isn't anything left to discuss.

> Why wouldn't having no const in the language
> be better /in terms of clarity/? Why is /not knowing/ from the
> function prototype that something isn't going to change so bad,

Consider this idiomatic Java or C#.

Bar bar = foo.getBar();
....
bar.setBogosity(42);

Did you just modify foo or didn't you? You will have to either read the
documentation or implementation to find out. Which will take ~2 minutes and
so cost 2N dollars. Feel free to pass bar to any function to complicate
things even further and increase the cost. Since such constructs are very
widespread your cost will grow up very quickly. This is the reason for the
advice "make Bar immutable to avoid this issue altogether.

> especially considering the fact that you /yourself/ give an advice to
> cast const away in the body of the function?

No this is not what I have meant. What I did mean was that if you need to
interoprate with const-less library that you know won't change the data you
can cast away const on the boundary. This is no different from thousands of
other 'boundary' issues (translating error handling for example) that you
need to do on module boundary.
This and implementing non-const function in terms of const one are the only
acceptable uses of const_cast. And yes the latter is a language deficiency.

> Furthermore, why is it "good" that to implement an STL-like container,
> you need to implement an iterator and a const_iterator?

First of all you don't. I have plenty of read-only containers that only
supply one.
Second, so what? In one case you specify how to iterate sequence of
read-only entities and in another how to iterate sequence of things that
can
be changed. They don't even have to do the same thing (though STL tries
hard
to limit our freedom there)

> What happens
> if you have a std::vector-like container that can get the storage
> pointer from outside (as opposed to allocating it in the constructor),
> and we want to attach a const storage pointer to it in some contexts
> and non-const storage pointer in other contexts? Should we have a
> vector and a const_vector?

I don't completely understand what you mean by this. What I think you are
trying to describe can be modelled as Container<T> and Container<const T>.

> Writing smart pointer classes to avoid the problems with const is what
> I call fighting the language.

Other people would call it extending. But I do agree that having other
kinds
of pointers in the language itself would be better.

> IMO, the readability gained from understanding which objects
> can be modified by a piece of code is lost many times over by having
> much more code.

FUD. One tiny class that is polished once and never changes again is not
"much more code".

> Writing smart pointer classes to deal with const correctness is also
> manual work.

Once for a given organization. With some luck you can probably find one
already written. The total cost won't exceed ~60N.

> a) Garbage collectors don't work with C++ in practice, see another
> sub- thread.

How did you arrive to this conclusion? I distinctly remember people here
posting that they do in fact use garbage collectors.

> b) This has nothing to do with garbage collection.

Oh it does. If you are going to new everything you better have some form of
GC.

> If C++ didn't have
> const like most languages, and you had a void func(ClassName&), then
> func(ClassName(args)) would work just like func(new ClassName(args)).

You complained that it is impossible to write code while avoiding const. I
have shown you the way which is exactly what you do in other languages. Now
you seem to want to use value-based semantics for this too. Sorry, but you
can't. You don't have value based in most other languages either so I am
not
sure what is there to complain about.

>>> and if you need libraries that use const, it will get worse):
>>
>> Why worse? Do what you do every two lines in other languages: cast.
>
> I don't do it in other languages,

Really? No casts in Java?

>> Are you talking about the language or particular compilers?
>> Incidentally this confusion is all over your site.
>
> This isn't "confusion", either. For someone actually writing software,
> a language is only as good as the best implementations you can use in
> practice. And C++ is old enough and popular enough to give people
> enough time and motivation to create high quality implementations.
> Whatever doesn't work smoothly by now has low chances to improve
> significantly in the future.

Very wrong. Commercial vendors with large resources all almost abandoned
C++
since mid 90-s to pursue their Java, .NET and other dreams. If
Microsoft, to
give one example, would have poured one tenth of the resources they devoted
to .NET into their C++ compiler and libraries we would have seen very
different results now.
In any event blaming language for poor quality implementation without
saying
so prominently is at best intellectually dishonest. Why didn't you put C++
COMPILERS SUCK on your site?

>
> The problems with the build speeds of templates are inherent to the
> design of this language feature:
>
> http://yosefk.com/c++fqa/templates.html#fqa-35.12

I won't even bother to comment on this one except pointing out that
there is
at least one compiler (produced by 3 people with tiny budget I believe)
that
does implement export.

> I've spent lots and lots of time working with C++, and I understand
> your position on this issue quite well. You value precise static
> typing (which you call "clear expression")

Nope. Static typing is not "clear expression", only a part of it.

> very high and the actual
> problems of "implementations" very low,

No. I know and care about practical problems as well as you do and I often
have to work around them too. However, I don't think they are as
troublesome
as you claim.

> nor do you acknowledge that
> these problems really come from the language definition.

They are in some cases. However, your site and postings here don't contain
anything to confirm or refute this fact.

> You're also
> very tolerant to the need to write wrapper code, and prefer the
> benefits of precise modeling to the drawback of having more code to
> read and maintain.

Not exactly. I distinguish between stable library code and volatile
application one. Stable library code (like for example a smart pointer) is
written once and never changes. Its maintainance costs are very close to
zero after that. (Change in a compiler might sometimes cause a change but
this is again small fixed cost). Application code changes often and there
indeed more code means more maintenance costs.

--
Eugene

David Abrahams

unread,
Nov 2, 2007, 10:23:00 PM11/2/07
to

on Fri Nov 02 2007, Yossi Kreinin <yossi.kreinin-AT-gmail.com> wrote:

> What I claim is this. Most languages don't have const and most
> programmers consider this tolerable. C++ does have const, which
> according to most programmers helps in many simple cases. However,
> when you have levels of indirection, you simply can't do "the right
> thing", which can be objectively demonstrated, and programmers end up
> copying objects or casting pointers or making everything template,
> each which has it's own problems and is a way to work around the
> problem, not solve it.

The problem that "making everything a template" addresses would not be
solved by transitive const, as you seem to imply. Templatizing can be
used to deal with the kind of code repetition you get in the const and
non-const versions of a class' operator[], where you can't just use
one non-templated declaration because you need to return either a
constant or a mutable reference depending on the constness of *this.

Frankly, any solution to that problem is going to look a lot like a
template, because that's the only way to serve up different return
types from "one function." I could imagine some nice syntactic
shortcuts that would allow me to put a templated const-ifying shim
over a non-templated function without ever writing angle brackets, but
conceptually, there's still a template there. Incidentally, I wish we
had cv-qualification as a template parameter ;-)

As for the issue of casting pointers, the way const works in C++
almost never forces you to cast; if you often find yourself casting
away const in C++, you're almost certainly doing something very wrong.
That sort of thing should only happen in the gnarliest low-level
systems code and when interfacing with legacy systems that don't know
about const.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

Yossi Kreinin

unread,
Nov 2, 2007, 10:27:23 PM11/2/07
to
On Nov 3, 12:58 am, khall.ggr...@gmail.com wrote:

In general, in this message you argue with those of my statements that
aren't supposed to describe "facts" in the sense that they can't be
proved/refuted formally or experimentally in a feasible way, so I
won't counter in these cases.

Note that most of the FQA answers don't claim that the FAQ answers are
wrong, just mention things it omits (which I think is fairly important
because you can do just about anything using omissions). One example
where this is not the case (the FQA actually /corrects/ the FAQ as
opposed to adding information) is on the "opaque pointer" issue:

http://yosefk.com/c++fqa/class.html#fqa-7.5

>
> [6.1] "Is C++ a practical language?"
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>

> I strongly disagree with that. All you need to do is go to
> SourceForge.Net and go to "software development"->"Debugggers" and see
> how many items are related to C++.

The availability of many tools doesn't mean they are good, just that
people need tools (which is obvious - C++ is used quite widely). Lots
of free debuggers are based on gdb, which means that /most of them/
can't place breakpoints in templates, for example (some vendors hack
on gdb to fix some of the problems, apparently without committing the
changes to the main branch).

> Visual Studio 6.0 to Visual Studio
> 2008 offer incredible capabilities in debugging, with VS2008 showing
> many very cool new features. There are many non-MS and *nix tools
> available too: CodeBlocks, KDevelop, gcc, BoundsChecker, Electric
> Fence, and the list goes on and on and on....

I used VS a lot. For starters, auto-completion and definition look-up
has never been very reliable. I don't blame VS; the problem it faces
is extremely complicated. Free tools doing similar things are a bad
joke.

All of this is of course qualitative, and code bases may exist where
the problems manifest themselves less than in other code bases.
Typically, the amount of problems in this department is proportionate
to the complexity and the number of syntactic C++ features used.

The debugger's problem is an easier one, since the program was already
compiled and all references were resolved in a lengthy process.
However, things like parsing expressions in data display windows will
still cause problems (the debugger has to parse C++ type
specifications, expand macros and typedefs, etc.; some of it usually
does not work).

Note that VS only works for Windows-based targets, and many of them
aren't. In the area of cross-platform/embedded development, tools
based on gcc/gdb/Eclipse combos are quite common; I'd say they are
quite poor at the moment compared to VS. Proprietary debuggers are
better, but they have smaller user base than, say, VS, and will
normally fail in more kinds of creative ways.

>
> You also say "C++ is not "mature" in the sense that different
> compilers will interpret it differently". While the very advanced
> parts of the language may act different on some compilers, most behave
> similarly for parts of the language that produce defined behavior.

There's plenty of undefined behavior in C++ (consider visibility of
overloads and template specializations at the point of usage), and
lots of dark areas easy to stumble upon in relatively naive code
(overload resolution and implicit casting, for example). Even porting
from gcc 3 to gcc 4 may be quite tedious. /All/ compilers I've used
crashed on C++ code of moderate complexity (nothing approaching the
levels of boost, for example; boost libraries are extensively tested
on many compilers and contain lots of compiler-specific build logic).

>
> C++ is powerful (boost::spirit and
> boost::mpl are amazing examples)

I find "Amazing" an appropriate term; what interests me is "wise to
use in real world prototype or production code", though.

> And what does a managed environment buy, do tell? If a program is ill
> formed in a managed environment, the environment may tell you more
> information when the programs behaves incorrectly, but the program
> still behaves incorrectly.

Not just that - the execution will /stop/. This is tremendously
helpful in real life. Experience with /all/ software vendors shows
that you invariably ship ill-formed programs (and I'd say that most
"stable" program versions out there are /still/ ill-formed, it just
manifests itself very rarely). Denying this (as done by proponents of
"performance" as well as many professional methodologists) is very
counter-productive.

Now, in an unmanaged environment, many ill-formed programs keep
running after committing the first "crime" (actually performing an
illegal operation). This means that understanding the problem will be
harder since the program "covers its traces" (destroys the objects
related to the illegal operation), and may in fact never crash, but
instead crash a plane or zero out your savings in a bank account,
depending on the domain.

Of course in many domains unmanaged environments are unavoidable, and
I happen to work on this kind of code myself. However, having lots of
experience with the problems it causes, I claim that most people
greatly underestimate them.

> The managed environment may have garbage
> collection, but there are garbage collectors available for C++ too.

In theory; did you use one in your project? See also adjacent sub-
threads.

>
> And, by the way, C++/CLI does run in managed environments.

C++/CLI is not C++. "Managed C++" isn't C++ either, and some of it
compiles to unmanaged code; I didn't check if the same is true for C++/
CLI.

>
> You also state "Providing C++ interfaces to a software component is
> impossible in practice due to lack of compile time and run time
> interoperability". That's just simply wrong! My proof is the vast
> number of sucessful C++ projects. What's your proof?

Try to compile libraries in gcc and use them with VC++.

Your proof shows that you can ship C++ software (empirically true for
many languages you wouldn't use), not libraries with C++ interfaces.

> There's only one doubly-linked list type, only
> one resizable array with O(1) random access, only one double-ended
> queue, etc....

There's a huge amount of pre-standard (and post-standard) vector,
list, string and map classes, used in lots of software.

> My
> biggest complaint about most recent languages is their lack of
> 'const'. I'm glad D didn't sacrafice this!
>

D changes the definition of const, however, which has the potential to
solve serious usability problems with the C++ const (although I can't
cite my experience or analysis proving it); see adjacent threads.

>
> [6.14]
> ~~~~~~
>
> You state "Asking about things specific to C++ is not very useful."
> That's not true at all. You acknowledge earlier in the FAQ that it
> takes a long time to become proficient in C++. If a company already
> has a C++ project that must be maintained, then simply grabbing a
> talented person who isn't familliar with C++ is a poor buisness
> decision.

I agree; if you want someone to maintain a complicated C++ code base,
you may need to ask these things. But this is far after the point
where language choice is relevant for you, and if your code is
complicated enough for deep knowledge of C++ to matter, you've already
made a poor business decision previously.

I know may kinds (and "instances") of great programmers who'll
probably never know C++ anywhere near my level, which is not at all
close to "completeness". Losing the whole lot of them to your
competitor is a very bad scenario:

file:///E:/sw/fqa/faq.html#faq-4

> First, you can't really tell us what someone else means. That's like
> me telling you that what you really mean by your FQA is that "C++
> sucks", despite your disclaimers to the contrary. This sounds just
> like another rant.

/Of course/ I can tell what someone else means, by analyzing all
places where something is said and summarizing the kinds of contexts.
So can you; in your example, I'd counter with the fact that there's
lots of factual information in addition to the clearly subjective
kind.

> The FAQ afterall is not some hallowed document -- it's just one man's
> opinion. Other people will agree with the FAQ to varying degrees. I
> agree with most, but certainly not all of it.
>

Well, so is any document, including the FQA...

Walter Bright

unread,
Nov 2, 2007, 10:29:43 PM11/2/07
to
James Hopkin wrote:
> On Nov 2, 4:42 pm, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
>> What about when you need a subset of objects kept in std::vector or
>> std::list, and the owning collection is about to be deallocated,
>> because you only need some of the objects, but not all of them
>> anymore?
>>
>> http://yosefk.com/c++fqa/dtor.html#fqa-11.1
>>
>> Do you copy the objects, which has a performance penalty, or keep
>> collections of pointers, so you have no RAII coming from the standard
>> library to help you with exception safety anymore, or do you use smart
>> pointer classes which do reference counting, which breaks when you
>> have cyclic references?
>>
>
> With std::list you can use splice. As for contiguous containers
> (vector, deque) - that's the tradeoff you make for the advantages of
> contiguous allocation.

You can do slicing (taking a contiguous subset of an array of objects)
with contiguous allocation if you have garbage collection. Slicing of
strings and such has turned out to be an enormously effective technique,
as it requires no allocations, copying, reference counting, templates,
etc. It's a big advantage of gc that is not practical with explicit
memory management using smart pointers, reference counting, RAII, etc.

> None of the other popular languages give you
> this ability (or only for a very restricted set of types like C# value
> types).

The D programming language offers slicing for all array types.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Yossi Kreinin

unread,
Nov 2, 2007, 10:42:41 PM11/2/07
to
On Nov 2, 6:35 pm, Hyman Rosen <hyro...@mail.com> wrote:
> vector<T *> vp;
> vector<const T *> vcp;
>
> You know, you keep carping about how these two types are
> incompatible, as if it were a given that this should not
> be so. But in fact, the two classes can be very different,
> because C++ allows specialization. It's not the case for
> std::vector, but it can be the case for other classes.

I am aware of the freedom you have with template specializations, as
well as some of its consequences:

http://yosefk.com/c++fqa/web-vs-c++.html#misfeature-3

I also think typing problems discussed in this sub-thread reproduce
with built-in arrays, which have somewhat similar pairs of
"incompatible" types - an array of const pointers is /not/ a special
case of an array of pointers (and it shouldn't be since that would
allow you to fill an array of non-constant pointers with pointers to
constant objects and then modify the objects in another context):

http://yosefk.com/c++fqa/const.html#fqa-18.17

I'm not questioning the reasons making these pairs of types
"unrelated"/"incompatible", just claim that it makes const unhelpful
when you have levels of indirection. I prefer to use std::vector<T*>
rather than T** in the examples since otherwise many C++ programmers
would simply recommend to avoid built-in arrays.

Alf P. Steinbach

unread,
Nov 2, 2007, 10:42:42 PM11/2/07
to
* Yossi Kreinin:
> On Oct 29, 3:22 am, "Alf P. Steinbach" <al...@start.no> wrote:
>> A Frequently Questioned Answers is a very good idea, and I commend
>> the
>> effort you've put into this.
> Thanks!

>>> One thing I'm definitely interested in is whether I got
>>> any facts wrong, and if I did, which ones.
>> Well, most of it. ;-)

>>
> Well, I meant "facts" :) My definition of "facts" in this context is
> here (together with the first factual correction submitted by a
> reader):
> http://yosefk.com/c++fqa/web-vs-fqa.html

Why not quote that definition: «I mean statements which can be proved
wrong or refuted by a practically feasible test.»

Starting with the first sentence of the first FQA item, numbered 6.1,
«C++ is not "mature" in the sense that different compilers will
interpret it differently, and C++ modules built by different vendors
will not work with each other"». If the latter part of the sentence
is meant to be existentially qualified, that it's possible to write
modules that will not work with each other, then it's meaningless. If
on the other hand it is meant to be universally quantified, that it's
impossible that different vendors write modules that work with each
other, then that's demonstrably false. So as it stands it's either
meaningless or false. It is however not difficult to understand what
is probably meant, that C++ lacks an official (e.g. ISO) standard for
compiled code (an ABI). As it is, one must rely on de facto
standards. But this best-meaning interpretation doesn't help the fact
that the FAQ's statement as it stands is either meaningless or plain
false.

Next sentence, «C++ is not "well-supported" in the sense that

development tools for C++ lack features and are unreliable compared to

other languages.» I would tend to agree with the features
(functionality that requires parsing of C++ code is generally lacking,
due to the complexity of a C++ parser) but not with reliability (on
the contrary). However, this is just opinion that cannot be proven or
disproven. It may be that the author has only used low quality tools.

Third sentence, «These things make one ask "Am I the first one trying
to do this?" all the time.» If this sentence is about the author's
experience then it is presumably true, but not falsifiable. :-) If it
correctly relates the author's experience, rest assured that you're
not the first, and that there are also good tools for C++.

And so on: of the first three sentences, one was either meaningless or
false, and two were not falsifiable.

>> One main high level issue that the FAQ gets wrong is the purpose of a
>> programming language. In general it's a good idea to use the most
>> appropriate language for a task, but your FQA seems to assume that
>> somehow everything has to be done in C++.
> The FQA doesn't say that everything has to be done in one language
> anywhere, and I use several languages myself. The FQA says that /
> nothing/ should be done in C++. This does not imply that "if C++ were
> good, you'd have to do anything in it" or anything like that.
>> Garbage collectors such as the Boehm collector work well.
> One problem is that several libraries may be built to use different
> garbage collectors. Garbage collectors are not entirely non-intrusive;
> they may ask the user to provide some kind of layout information or
> finalization functions, for example. Having many of them in one
> program is thus problematic.

I wouldn't know because I haven't used garbage collection with C++.
The only person I know who does use garbage collection in C++ is James
Kanze. And he has not reported any such problems, instead
recommending garbage collection to newbies.


> Non-intrusive garbage collectors have false positives (some things
> look like pointers, but aren't), and fail when people use formally
> illegal, but normally working techniques like int* one_based_vector =
> (int*)malloc(sz)-1;

I hope James will answer that.


> One thing C++ garbage collectors can't do is heap compaction. AFAIK
> this increases the upper bound on the memory consumed by a program
> with the peak sum of allocated object sizes equal to M from M to
> M*log(max_block_size), which is a huge difference for realistic values
> of max_block_size.

There is nary a computer science problem that can't be solved by
adding a level of indirection. Modern computers typically have built-
in memory management that adds just such a level of indirection. In
the old days before that hardware became commonplace on PC's we had to
use handles to memory (instead of direct pointers), in order to allow
compaction.

With extreme fragmentation and large underlying page size it would be
possible to consume a lot of memory, as opposed to address space.

However, I don't think that's something that a language solves for you.

> However, for
>> many C++ programmers garbage collection is not desirable, because
>> while
>> it can ensure that memory is always reclaimed, it leads to other
>> problems.
> If you refer to /hard/ real time applications (most large ones are
> really /soft/ real time), than AFAIK operator new has the same
> problems in terms of worst case performance. If you refer to resource


> leaks when resources!=memory, then I have to point out that the vast

> majority of resources /is/ memory, and very basic language constructs


> (such as new) depend on being able to acquire memory, and not having a
> simple solution for memory management is likely the single biggest
> problem with "low-level" languages when you try to do "high-level"
> work in them (but C++ is promoted as a pretty high-level language).

> IMO not acknowledging the difference between "memory" and "resource"
> is a manifestation of perfectionism ("solutions that only solve 95% of
> the problem are worthless"), and there's plenty of such things in C++.

Unpredictable and jerky response time is one consideration, yes. But
I was mainly thinking of the impact on design when objects hold
references to non-memory resources. In e.g. Java this leads to
"destroy" methods all over the place, where a call to such a method
zombifies the object. This in turns leads to checks of whether the
object is a zombie, resulting in complication, and undetected invalid
usage of the zombified object.

C# helps a little with its "using" statement, but that's mostly
cosmetic.

My ideal garbage collector for C++ would be one that addressed one
problem only, that of doing actual memory reclamation at idle times.
Such a garbage collector would not invoke destructors. Instead,
memory freed by operator delete or C free would by default be passed
(constant time) to a list, traversed by the garbage collector at
suitable times.

>> >From the FQA:
>>> And what if you make a mistake, one single mistake with all these
>>> deletions?
>> with proper RAII, if an object is still alive, then its class
>> invariant
>> still holds.
> RAII does not at all prevent problems with dangling references. If you
> keep a pointer into an object in an STL container, which acquires
> memory in the constructor and deallocates its memory in the destructor
> (which is what RAII means), and the container dies, you have a
> dangling reference. Which is why C++ programs will frequently make
> themselves private copies of objects - pointing to something you don't
> "own" is dangerous.

You're right that RAII does not prevent problems, it just helps solve
them. Garbage collection doesn't prevent problems either (in
particular early versions of Java's Swing library tended to leak
memory by keeping references to old objects in singletons, garbage
collection didn't prevent that). The solution is in general not to
copy data. Since you assert that that's a common solution it seems
you have been exposed to some really bad programming practices. But
that's not a feature of a language: incompetence is a feature of
programmers.


> You can implement reference counting on top of RAII, making all your
> pointers "smart" (and having the usual trouble when integrating with
> other libraries of equally smart though different, or, most often,
> dumb bare pointers). Then you'll have the problem with cyclic
> references (which, by the way, is AFAIK the single case when a GC
> could leave "zombie objects" around - and note that it took us way
> less work to get to this stage).

Curiously, I've never had a problem with cyclic references in C++. On
the other hand, it is true that blind application of unsuitable
reference counted smart pointers runs a good chance of creating such a
problem. And I've helped resolve such problems created with reference
counting in other languages, mainly Visual Basic. Again it's not the
language's fault, but generally incompetence on the part of the
programmer.

And no, GC doesn't create zombie objects (programmers do), and no, it
doesn't have anything to do with cyclic references.

A zombie object results from manual logical destruction (in order to
release non-memory resources), leaving an existing, zombie, object.

Cheers, & hth.,

- Alf


--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Yossi Kreinin

unread,
Nov 3, 2007, 11:39:00 AM11/3/07
to
On Nov 2, 6:36 pm, khall.ggr...@gmail.com wrote:
>
> Recompilation can be avoided by using the PIMPL pattern:http://en.wikipedia.org/wiki/Opaque_pointer.
> This problem is not unique to C++; C has similar problems.
>

I'm aware of this idiom, and of the solution where you have an
abstract class to decouple interface from implementation, however,
both require you to write wrapper code:

http://yosefk.com/c++fqa/inheritance-abstract.html#fqa-22.1
http://yosefk.com/c++fqa/heap.html#fqa-16.21

C does have this problem, however, its grammar is much simpler, so
recompilation caused by changing the definitions of structures is less
costly:

http://yosefk.com/c++fqa/defective.html#defect-2

In C, there's also a simpler solution - incomplete types (naturally,
you can use it in C++, too if you avoid normal C++ classes and class
members):

http://yosefk.com/c++fqa/class.html#fqa-7.5

Note that if you program in C and place structures in header files, at
least module functions which aren't part of the interface (which
correspond to private methods of C++ classes) can be added/removed/
undergo changes in the prototype without triggering recompilation.
Only changes to structure variable definitions will cause a rebuild.

> First off, there is no restriction that says operators must return by
> value.

I was referring to operators that /compute new values/: a+b*c, the way
built-in operators do. Overloading operator<< to do formatting is a
very special case - if it weren't in the C++ standard, many would
claim that it makes code less readable, defeating the purpose of
operator overloading (I can't quantify "many", but a common advice is
to overload operators such that their function resembles that of built-
in operators).

>
> Also, smart pointers can be used when needing to return "new'd"
> pointers. There's one in the current standard (std::auto_ptr) and a
> few more that will be in the next standard.

Such a smart pointer should do reference counting to avoid unnecessary
copying in the case where a+b*c compute new matrices. If the matrices
are known to be small enough (say, 5x5), new'ing them and maintaining
the reference counts can defeat the purpose, since it's comparable to
simply copying the objects returned by value. AFAIK the only simple
way to guarantee fast execution is something like:

matrix prod, sum; //a built-in fixed size array or a template
mat_mul(prod, b, c); //passed by reference
mat_add(sum, prod, a);

Of course you can have very elaborate library code trying to optimize
various cases of matrix expressions and still have operator
overloading (copying is one problem, mapping the two operators in a
+b*c to a single specialized function optimizing this particular
expression is another one). I claim that if performance is important
to you, so is the ability to easily estimate it by looking at the
code, and hence you'd rather use more straight-forward methods in
production code, and keep the operators in the comments and prototype
code.

>
> The RAII idiom works very well.

RAII works well when there's a single resource owner, and others don't
have to reference the resource after it dies. Most of the "resources"
are simply objects in memory, and sometimes their life cycles are
complicated. You end up copying the objects (slow) or maintain
reference counts (doesn't work with cyclic references):

http://yosefk.com/c++fqa/dtor.html#fqa-11.1

In other cases, you simply end up having dangling references.

I'm glad that it worked for you personally, of course others could
counter with their experience, but discussing the differences between
projects is hard and probably off-topic.

> Similar problems exist in other languages that have
> exceptions too. How does one release a resource in C# when an
> exception occurs? Via a 'finally' statement or a 'using' statement.

The thing is that in languages with garbage collection, "memory" is
not a "resource". This may sound "non-generic" and arbitrary; well, C
and C++ don't treat /stack/ memory as resource, and stacks do
overflow, and sometimes you wish you could handle it more gracefully
than it is handled in most C and C++ implementations. Anyway, the call
stack is special because function calls depend on it to work. Memory
is also a very special kind of resource, because new depends on it,
and the ability to create objects is a very basic one. Having to think
about the way they are disposed is really very tedious (this statement
is of course subjective).

Now, other resources are very different. They are typically more
scarce than memory, and you use less of them. They are handles; you
don't need to think about "resource fragmentation" (and memory
fragmentation is a big problem, especially when it comes to worst case
memory usage; a single built-in "dumb" pointer makes automatic heap
compaction impossible in C++).

And most importantly (and of course arguably :) ), the use of those
other resources /shouldn't/ be "transparent". If a module opens files
or database connections, the calling code should be aware of it at the
"social" (human) level, and carefully handle finalization. "Resources"
are a heavy thing; you don't want someone to mistake an object that
needs resources for a "lightweight" object and create a zillion of
them. Furthermore, it's frequently a good thing to separate the code
doing I/O (which many resources are related to) from the larger bulk
of code processing the data. Synchronization primitives are a special
thing, of which there simply should be few (see the advocacy behind
Intel's TBB library, for example).

> No language feature is 'evil' (the intent with which it is used may be --
> see obfuscated C contest ;] ).
>

The word "evil" is used by C++ FAQ Lite over and over again:

http://yosefk.com/c++fqa/picture.html#fqa-6.15

Many C++ programmers would agree in the sense that they'd go to great
lengths to avoid the features called "evil" by the FAQ, almost
independently of the degree of applicability of these features to the
specific problem at hand. Of course there are other attitudes, too.

> Note also that the reason std::string and std::vector exist is because
> the C facilities have their shortcomings: namely the inability to
> resize themselves. You're right, the std::string and std::vector are
> in some ways inferior to char* and arrays, but in some ways they are
> superior too. There's a tradeoff that must be considered.

I realize that, and in several places warn against "banning" the
criticized C++ features in local coding conventions or similar:

http://yosefk.com/c++fqa/const.html#fqa-18.1
http://yosefk.com/c++fqa/ref.html#fqa-8.6
http://yosefk.com/c++fqa/inline.html#fqa-9.5
http://yosefk.com/c++fqa/ctors.html#fqa-10.6

All I'm saying is that for me, a language user, having C features and
standard library duplicated almost entirely by new C++ features and
standard library causes trouble, while for the designers/promoters of C
++, this was quite beneficial.

>
> You said "C++ doesn't add any built-in types to C."
> What about 'bool'?

Strictly speaking, you are right, and I think I'll publish a
correction. This doesn't contradict the meaning of what the item about
no high-level built-in types in Defective C++ says, however. bool is
very close to the built-in types already in C in terms of its
behavior.

Somewhere else in the FQA there's an amendment about how C++ /is/
willing to add new built-in types if they're a new kind of pointer,
which of course doesn't cover bool.

> Also, depending on your definition of "built in", you could include
> all the standard library classes, which actually is quite a large
> number (std::string, std::ifstream, std::ostream, etc...)

This isn't in my definition of "built-in" in the context of the text
we are discussing - the text explains why:

http://yosefk.com/c++fqa/defective.html#defect-12

>
> Macros are not lame at all. They are useful when created domain
> specific languages (http://en.wikipedia.org/wiki/
> Domain_Specific_Language) as well as helping out sometimes with other
> tasks.

What I really meant was that C macros have well-known and unavoidable
shortcomings. They are more useful than nothing, and I use them myself
at times.

>
> By the way, macros and templates make C and C++ two of the few
> languages to be able to create DSLs that are parsed by the compilers
> themselves rather than having to build a program or library to parse
> DSL. That's nifty (and useful)!

I agree with the spirit (DSLs are useful), but not with the facts :)
With C macros and C++ templates, simple facilities like loops are
something either hard or impossible to implement, depending on the
context. Now, most languages don't have better macros (but Lisp and
Forth dialects do, for example). However, many languages have an eval
function or other ways to compile/execute code generated
programmatically, which gives more options than C++ meta-programming
facilities. Also, sometimes data-driven programming is a sufficient
"approximation" for DSLs (and even has its bonus points such as easier
analysis of the "code"), and many languages support data
specifications better than C++ (having list/dictionary literals helps
tremendously).

>
> I could continue on, but I need to go to bed now as I'm falling asleep
> typing.

Good night, and thanks for a friendly message! :)

Eugene Gershnik

unread,
Nov 3, 2007, 12:42:53 PM11/3/07
to
Walter Bright wrote:

> The const X would have read only access to mutable Y. There is no way
> to modify Y through const X. If you want to mutate Y through X, then
> X must not be const.

[...]

> Essentially, D doesn't support the notion
> of an object pretending to be const, only objects that actually are.
> The idea is that if you see a function signature that says its taking
> a const argument, it really truly cross-my-heart promises that it *will
> not* be mutating it.


I see. IOW in D const means physical rather than logical immutability
and it
is not orthogonal to object relationships.
Well these features are not something I am personally interested in since I
don't see any value in either. Actually I see great reduction in value
compared to C++ const.
Obviously YMMV

--
Eugene

Walter Bright

unread,
Nov 3, 2007, 12:42:58 PM11/3/07
to
Eugene Gershnik wrote:
>> The problems with the build speeds of templates are inherent to the
>> design of this language feature:
>>
>> http://yosefk.com/c++fqa/templates.html#fqa-35.12
>
> I won't even bother to comment on this one except pointing out that
> there is
> at least one compiler (produced by 3 people with tiny budget I believe)
> that does implement export.


Is there any evidence that export in practice improves build times?

I've looked in to implementing export. Based on how it must work, there
is not much prospect of it fundamentally improving build times. And
seeing how export has been available for several years now, yet has
failed to catch fire, doesn't auger well for its utility.

What is proven to substantially reduce build times are precompiled
headers (but p.h., in order to work, has to place restrictions on what
can be put into headers). Even so, templates have other fundamental
problems that cause something like the factorial template to run 10,000
times slower than compiled factorial code.

P.S. Yes, I pulled 10,000 out of the air, but if you look at what must
be done to instantiate a template, it's probably far too small <g>.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--