--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Why?
I personally always use implicit constructors.
I only use explicit constructors when there is something dangerous
going on, such as ownership transfer.
> The question is why do we have to (explicitly) declare these
> constructors as explicit to avoid type conversion, when this is
> something one would almost always want to do. Why isn't this feature
> disabled by default, and enabled by specifying this constructor as
> implicit?
>
That is your view. The people who formed the standard thought oterwise.
Now that is the established convention.
Regards,
Jyoti
I agree with Mathias and Jyoti.
That sounds more logical for most of the C++ developers I know.
By the way, *objectively*, would you have any good reason to suggest a
change about this ?
I would go even further than that and say that implicit type
conversion should have been left out of the language altogether.
While it make it a little quicker to write, it makes it a lot harder
to read.
Consider:
T x = ...;
f(x);
Without cross referencing all the signatures of f's overloads, with
all classes that have a constructor that has T as a parameter - the
reader will have no idea which constructor is being called and which
version of f is being executed.
-Andrew.
Simply a matter of history and the need to maintain legacy code.
You used the term "logical". But IMHO, it has nothing to do with
_logic_, it is simply a _chosen_ syntax convention. So maybe the
word you were looking for is "convenience"? I personally don't
find it convenient.
Mathias Gaounard wrote:
> I personally always use implicit constructors.
Your preference has nothing to do with the syntax used to express
the implicitness of your constructors. In a parallel universe
you would write "implicit Foo(int x)" for all your
single argumented constructors and have exactly the same behavior.
I personally always use explicit constructors unless I _really_
want implicit conversion.
class Some_weird_parser {
public:
Some_weird_parser(int p1);
};
class Another_weird_parser {
public:
Another_weird_parser(int p1, int p2);
};
Why can the first class be converted from an integer? Who would find
this operation useful?
But, we're stuck with the syntax...
Andrew Tomazos wrote:
> Without cross referencing all the signatures of f's overloads, with
> all classes that have a constructor that has T as a parameter - the
> reader will have no idea which constructor is being called and which
> version of f is being executed.
.... in which case it is a poor design made by the one who wrote "f".
It should be obvious what "f" does and what it takes for arguments.
And you don't need to cross-reference with all classes that have
an implicit conversion from T, just those that can be passed
to "f".
--
Dragan
Many guidelines I've seen recommend against implicit conversion, because
they are sneaky and can be dangerous or confusing; explicit conversions
are better in that regard. On the same ground, std::basic_string<charT>
doesn't have operator const charT*() but c_str(), std::bitset<N> doesn't
have operator unsigned long() but to_ulong(), etc.
Furthermore, the status quo is inconsistent: constructors that need more
than one argument are inherently 'explicit', but those that need only one
argument are 'implicit' by default and you have to make it 'explicit'
explicitly by the keyword. This means that adding or removing arguments,
or even default arguments, can change the 'implicitness' or 'explicitness'
of the constructor.
Y::Y(const X&, int); // inherently 'explicit'
Y::Y(const X&, int = 0); // becomes 'implicit'
explicit Y::Y(const X&, int = 0); // 'explicit' again
Y::Y(const X&); // becomes 'implicit'
explicit Y::Y(const X&); // 'explicit' again
The language would be more consistent and orthogonal if all constructors
were 'explicit' by default and we had an 'implicit' keyword instead.
Y::Y(const X&, int); // inherently 'explicit'
Y::Y(const X&, int = 0); // still 'explicit'
implicit Y::Y(const X&, int = 0); // becomes 'implicit'
Y::Y(const X&); // still 'explicit'
implicit Y::Y(const X&); // becomes 'implicit'
I don't think this change to happen in practice, though. This is a part
of the burden of backward compatibility that the language has to carry.
On the same grounds, there have been proposals that try to amend the
conversion functions by adding 'explicit' conversion functions that can
still be used for conversion but only if explicitly asked, e.g. in
static_cast; because implicit conversion functions are considered too
dangerous and recommended against, and have become pretty useless.
It's too bad that a language feature, into which many people's efforts
have been put, is rendered useless by oversight or lack of experience, but
it's too late for such a big change (explicit by default), unfortunately.
--
Seungbeom Kim
> I would go even further than that and say that implicit type
> conversion should have been left out of the language altogether.
> While it make it a little quicker to write, it makes it a lot harder
> to read.
>
> Consider:
>
> T x = ...;
> f(x);
>
> Without cross referencing all the signatures of f's overloads, with
> all classes that have a constructor that has T as a parameter - the
> reader will have no idea which constructor is being called and which
> version of f is being executed.
With the same kind of argument, you could say we should ban SFINAE
since there is no way to know which overload is the best match without
doing full type substitution, meta-function evaluation, etc. (SFINAE
can also apply to any expression in C++0x, making it worse)
The thing is, it shouldn't really matter to the callee which overload
is called as long as overloads have sensibly the same behaviour, at
least for the category of types concerned.
If it's for the debugging purposes, a debugger can find which overload
you're in without an issue.
If it's for static analysis, just implement the rules of the language
before analysing it.
> Many guidelines I've seen recommend against implicit conversion, because
> they are sneaky and can be dangerous or confusing; explicit conversions
> are better in that regard. On the same ground, std::basic_string<charT>
> doesn't have operator const charT*() but c_str(), std::bitset<N> doesn't
> have operator unsigned long() but to_ulong(), etc.
While implicit conversion operators are frowned upon in most cases,
implicit constructors not that much.
All the standard classes you're talking about have implicit
constructors, for example.
--
> The thing is, it shouldn't really matter to the callee which overload
> is called as long as overloads have sensibly the same behaviour, at
> least for the category of types concerned.
Both arguments rely to some extent on "good design" and "sensible
behavior". In a perfect world the language should allow the
programmer to do whatever they can imagine, and the programmer should
only imagine the Right Thing that make absolute sense to everyone.
Back in reality, what is an elegant abstraction to programmer A, seems
like a complete mess to programmer B. This friction is mitigated by
trying to keep a balance between on one extreme "allowing the cats to
run wild" and on the other "keeping them locked up in cages". In
general I suspect C++ is too far toward the former. Certainly
designers of the major post-C++ languages (Java and C#) agree with
this assessment.
-Andrew.
They _have_, but not _all_ their constructors are implicit, only those
that make sense to be implicit, which is a great minority.
examples:
explicit vector(const allocator_type &)
explicit vector(size_type, const value_type & value = value_type(),
const allocator_type & a = allocator_type());
std::basic_string has implicit construction only from:
- char array
- (in c++0x) initializer_list
How about:
explicit map(const _Compare & comp,
const allocator_type & a = allocator_type());
Need more proof?
IMHO, implicitness should be clearly visible in the code.
--
Dragan
>
> It's too bad that a language feature, into which many people's efforts
> have been put, is rendered useless by oversight or lack of experience, but
> it's too late for such a big change (explicit by default), unfortunately.
>
One of the costs of using a language is that it makes it harder to
improve it. There are several places where, if we were to design from
scratch, we would do things exactly the opposite way to the one that we
(have to) use.
e.g.
global declarations 'should' be internal to a TU unless explicitly
exported (and in this case we had the worst of all worlds in C though
this has been improved in C++ and the more recent version of C)
'variables' should be immutable by default.
member functions should be const by default
etc.
Instead because of the burden of existing code and existing practice we
have to do things the other way round.
--
Java and C# are certainly not idiot-proof either.
Bad code and practices can happen in any language, and trying to
prevent that at the language level is a loss of time IMO.
"You can do very bad things, yes, but it's your fault if you do." is
kind of part of the C++ spirit, also.
There are many C++ language features that are designed specifically to
catch "bad code and practices" at a language level. There is little
argument from professional programmers that we need at least some of
these.
The point of contention is where the balance lay. Usually the longer
someone has been programming and the larger the programming team they
work with, the more in favor they are of enforced restrictions on
doing "bad things". (For example, I've heard of blanket bans on
template programming and even operator overloading.)
Noone has claimed Java and C# are idiot-proof - but they are certainly
more restrictive than C++ in the way described.
-Andrew.
What about a string class with an implicit conversion from const char*?
Without that, all of the comparison operators must be overloaded for
string and const char*, or the user must construct a temporary string when
comparing with a string literal. The former is tedious to write, and
results in user code that is the same as if implicit conversion were used,
and the latter makes user code less readable.
> There are many C++ language features that are designed specifically to
> catch "bad code and practices" at a language level. There is little
> argument from professional programmers that we need at least some of
> these.
There are features to catch incoherences (const, for example, and even
the type system as a whole), but not really bad code and practices.
The fact that a feature, such as overloading, can be abused, is no
reason to remove it, since it can also have rightful uses.
Well, a standard library is not user code, so examples from it aren't
proof. But I agree with you -- I almost always use 'explicit'. In
fact, it surprises me that so many claim to not use it so much.
My one-argument constructors are only rarely "conversion-like"; the
argument is more often a size, a reference to some shared state and so
on -- not something which is similar to instances of my class.
And when they *are* similar, it is often the case that I created the
class precisely because I wanted to *avoid* conversions. For example,
to make class UdpPort distinct from uint16_t and other scalar types.
> IMHO, implicitness should be clearly visible in the code.
But you realize that changing it now would be impossible, right?
The only problem I have with it today is when I maintain badly written
code which doesn't have 'explicit' where it ought to. You can always
go through legacy code, add 'explicit' everywhere, and recompile. I
have found a few nasty bugs that way.
/Jorgen
--
// Jorgen Grahn <grahn@ Ph'nglui mglw'nafh Cthulhu
\X/ snipabacken.se> R'lyeh wgah'nagl fhtagn!
Those were examples to dispute the post I replied to. It stated
that there are many implicit constructors in std. My statement
is that there are implicit constructors in std _only_ where
it clearly makes sense.
> My one-argument constructors are only rarely "conversion-like"; the
> argument is more often a size, a reference to some shared state and so
> on -- not something which is similar to instances of my class.
>
> And when they *are* similar, it is often the case that I created the
> class precisely because I wanted to *avoid* conversions. For example,
> to make class UdpPort distinct from uint16_t and other scalar types.
>
>> IMHO, implicitness should be clearly visible in the code.
>
> But you realize that changing it now would be impossible, right?
Of course. My way to deal with it is to put "explicit" in _every_
explicit constructor, no matter how many parameters it has.
If it sounds stupid, the initial reason was - if I redesign
the constructor, for example to have only one parameter where
it had two, someone doing "diff" will clearly see what changed
and not be confused by the added "explicit" keyword. Now every
implicit constructor in my code is fairly visible.
--
Dragan
You might consider defining in your global header file:
#define implicit /* just FYI */
and then:
class C
{
explicit C(A);
implicit C(B);
explicit C(A,B,C);
...
}
This way you will notice when a constructor is accidentally implicitly
implicated as implicit, instead of explicitly implicating it as either
explicit or implicit. (Am I being explicit enough?)
-Andrew.
--
This is a good example of why implicit conversion is a bad idea. What
character encoding is the char* in? ASCII, Latin1, UTF8 or something
else? Is it zero terminated? There is no way to distinguish a string
literal from some other source of char* data, and it is unsafe to
assume some default encoding, because inevitably you will get data
converted between encodings incorrectly. Seen it happen many times.
Furthermore, when you are talking about string comparison operators do
you mean case sensitive or case insensitive comparison? Once again
you will need to assume some default if you overload ==, <, etc.
Better to have named member functions in the string class instead.
In general it is a much better idea to require the programmer to be
explicit and unambiguous about what the code means. It avoids reader
confusion, probably the leading cause of bugs in team programming.
-Andrew.
Well that depends on how you distinguish "incoherences" and "bad code
and practices". If I put 10 programmers in a room they will not agree
on where the line is drawn between the two, and what should be banned
versus what should be allowed sometimes when used appropriately.
I would therefore argue that the spectrum of programmer error,
starting from syntax errors, and all the way up to poorly-designed
object models and crappy UIs - forms a continuous scale.
Programming language designers decide where they "draw the line" on
that scale. C++ has its line. Java and C# have their lines.
The position I offer is that perhaps the C++ designers aimed too low
with their line. Certainly they have admitted to inheriting some bad
decisions from C, and had constraints from the backward-compatibility
requirement - however I still contend they could have gone further in
this regard. Requiring explicit type conversion is one example of
this.
-Andrew.
Yes, of course they could have done but, being human, they did not think
of the idea of a qualifier (be it explicit or implicit) until after far
too much code had already been written. When we (and I speak as a member
of WG21 at the time) decided that we wanted to make single argument
ctors more secure, it was far too late to make the default explicit.
Indeed, at that time we did not think it necessary or desirable to allow
conversion functions to be declared as explicit. Since then we have come
across some places where that would be helpful.
WG21 tries to do the best it can for the C++ programming community but
we lack time machines and are therefore constrained by what has already
been written.
Doesn't this suggest that C++ should fork to a new language? Leave alone
what there is already and start a C+++ as a different language (_not_ a
revision).
Opportunity would then be there to clean up the legacy mess & avoid the
horrible plasters that are being applied often to make the core language
work with a _library_ (STL) better?! Deleting references & starting again
would tidy up a lot of mess. Banning by default implicit type conversions
etc. etc.
C++ compilers will still exist to compile legacy code. No changes needed.
C+++ compilers would handle the new code. It would be trivial to link code
between the two as it is now with C/C++.
Programmers can then _choose_ the language they want to use without having
the new features forced on them by the compiler vendors adhering to the
evolving "C++" standard - which do include breaking changes especially where
a vendor is removing an extension (MS anyone!).
Flame wars about new school & old school would vanish. World peace would
ensue... perhaps not but the evolving C++ hardly relates to legacy C++ &
should fork.
{ He he, look up the earlier "D" threads in this forum... ;-) -mod }
{ Apologies, mod comments should only be statements of facts. -mod }
Chris
But isn't that exactly what Java and C# are? Or put another way, how
exactly would C+++ differ from Java and C#? As far as I can tell they
are what most people want if you design a language and are allowed to
make breaking changes to C++.
-Andrew.
> But isn't that exactly what Java and C# are? Or put another way, how
> exactly would C+++ differ from Java and C#? As far as I can tell they
> are what most people want if you design a language and are allowed to
> make breaking changes to C++.
Different people have different ideas of what the perfect language is
if you remove any compatibility restrictions.
I for one am fairly ok with C++ as it is.
There are only a few things I find annoying that can't be fixed due to
compatibility (C arrays and integer promotions, for example).
I utterly despise Java and C#, however (the latter being somewhat
better than the former IMO)
Well anyone is free to create a new language and make it as compatible
as they like to an existing one however that is not the task of WG21 who
are specifically required to maintain the C++ programming language.
I would also find it very confusing to have a language that was sort of
like C++ but with a whole bunch of defaults changed. It is already hard
enough untangling C, C++, C#, Java and Microsoft's proprietary dialect
of C++. And yes, I know about D and if that gains popularity it will
make the poor programmers life even harder in respect of handling
mixtures of new and old code.
No, those are not good examples. Maybe D is, but I'm not too familiar
with the language to make such a statement.
The idea would be to disregard all "we cannot change that, it will
break the existing code" reasons - and make things right and clean.
--
Dragan
What for? That you may remember to write explicit on your ocnstructors
or that you may remember the awkward declaration syntax or that you
might remember that ptr in char* ptr really is a char const*?
there are many small quirks in C++, but really that is not enough to
throw it away. C++ is a very useful language and the quirks are not
greater that you learn to live with them.
>
> But isn't that exactly what Java and C# are? Or put another way, how
> exactly would C+++ differ from Java and C#? As far as I can tell they
> are what most people want if you design a language and are allowed to
> make breaking changes to C++.
How they differ? They have not many similarities when you come to
think of it. Those languages don't allow low-level features, they
don't offer really generic meta-programming facilities, and they have
no destructors just to mention a few things. And while I can't talk
for C#, at least Java in practice have severe performance problems
(time and memory), excluding them for consideration in some
progrsamming fields.
That said, I have no doubt that Java and C# can be good choices for
some environments and some types of programs. They are just not as
generic as C++.
/Peter
This does not conflict with my point. The changes that are coming in C++ are
turning it into a different language. MS found out to their horror that
there are an aweful lot of VC6 developers out there because none of them
wanted the breaking changes & uncertainty of going to VC7! (About 50% of
atendees at a MS developers workshop apparently)
Is extra keywords, operators & tighter STL binding + CLR and enabling ever
sophisticated tempate meta programming _maintainance_ of C++? I suggest it
is a new language as C++ was to C, but because the name is the same there is
no differentiation by compiler vendor or source code files. People will
follow the fork (or not) _if_ the C++ compiler vendors release C++/C+++
compilers that compile both seamlessly and link in the _same_ project.
It is impossible to deny that the language is morphing. No new lanugaue will
gain consensus unless all the vendors are on board and eventually it gets
standardised. If the "official" C++ forks, compiler vendors will have
compilers that will cope with c++ & "c+++" in the same project. "C+++" could
even standardise filename extensions.
> I would also find it very confusing to have a language that was sort of
> like C++ but with a whole bunch of defaults changed. It is already hard
> enough untangling C, C++, C#, Java and Microsoft's proprietary dialect
> of C++. And yes, I know about D and if that gains popularity it will
> make the poor programmers life even harder in respect of handling
> mixtures of new and old code.
It is dead easy to handle C like languages. I'm sure Java, Javascript, C#
and PHP for example all use C like syntax for precisely the opposite reason
of it being hard. I use them daily as do 1,000s of other developers with no
issues at all.
Dev teams over the world are sticking with specific old versions of
compilers to force stability as the standard is changing the language. C++
of proposals barely resembles C++ described in Stroustrup 2nd edition. There
are lots of people who want C++ as it is now but with active development on
their compilers (bug fixes, performance updates, new processor support etc.
but no C++ language change). Solution: C++ should fork.
The committee are _uniquely_ placed to be able to suggest a course of action
to clean things up for the next 20-30yrs of C++ use.
Chris
OK, so make it a member template that accepts a char const (&) [N]. But
this is tangential to the point, so maybe a better example is a large
integer class with an implicit conversion from an int; repeat my same
argument above with this.
> Furthermore, when you are talking about string comparison operators do
> you mean case sensitive or case insensitive comparison?
You seem to be thinking this was about strings in particular, rather than
implicit conversions. See my example using a large integer class instead,
as this should help you focus on the point.
I'll just add to this... Most discussion about features that
someone feels should be changed, end by people stating
that "it would break old code". But this can only be
a good argument when speaking about core features, like
references or whatever would require unacceptable
amount of changes to the existing code.
What is the problem of making _some_ features of C++ as
deprecated in C++0x, with the goal of removing them
in the future, say 3-10 years. There is no argument
that would persuade me that this wouldn't be practical.
Broken old code? Well if it's such a great library
and you rely on it - fix it, you have 10 years for it!
Almost (although not the same) as if Linux kernel would have
an imperative not to break old drivers.
I'm going to get flamed for this, but before that,
just think about it - it's a logical step in cleaning
things up and making progress...
At least if you make an argument that "it would break
old code", please, please explain why is it unacceptable
to deprecate it.
For example, this explicit/implicit syntax can be
fixed by deprecating "explicit" and adding a new keyword
"implicit". I bet a good sed-guru would be able
to fix "the old broken code" with just a few lines
of script in much less than 10 years. (And now
I'm going to get flamed for proposing a new keyword :-)
--
Dragan
> For example, this explicit/implicit syntax can be
> fixed by deprecating "explicit" and adding a new keyword
> "implicit". I bet a good sed-guru would be able
> to fix "the old broken code" with just a few lines
> of script in much less than 10 years. (And now
> I'm going to get flamed for proposing a new keyword :-)
>
No, not for proposing a new keyword but for suggesting that it would fix
the problem. What you are suggesting would actually make the problem
worse. It would mean that every single argument ctor (including those
that have all, but possibly the first, parameters defaulted would need
to be be qualified as either explicit or implicit. The chance that
explicit would be removed under such circumstances would be as close to
zero as makes no difference.
--
--
Actually, technically, it changes waht 'C++' refers to because in the
Standard's world standards are replaced and ISO/IEC 14882:2003(E)
replaced ISO/IEC 14882:1998(E).
>
> It is a cultural convention to use �C++� as a �variable
> expression�, not as a �value expression�, i.e., the meaning of
> �C++� depends on the time this name is being uttered.
Yes, but more than a cultural convention it is what ISO requires. We
often use cultural techniques to circumvent this replacement (e.g. C90
versus C99, C++03 versus C++98) but as far as the Standards world is
concerned there is one single language called C++ whose definition
changes over time (much the same way that natural languages change over
time: 'He is gay' had a very different meaning in 1900 than it does today.
>
> This can be compared with a name like �Zermelo-Fraenkel set theory�,
> which has a time-independent meaning (AFAIK).
You sure about that? :-) Even mathematical theories develop and get
refined over time (sometimes because we discover that they are palin wrong)
--
Note that robinton.demon.co.uk addresses are no longer valid.
If anything this argument supports WG21 in its attempts to avoid
breaking existing code.We spend a good deal of time trying to minimise
the impact of changes on existing code.
<snip>
>
> The committee are _uniquely_ placed to be able to suggest a course of action
> to clean things up for the next 20-30yrs of C++ use.
>
>
And they also have a serious responsibility wrt existing code bases.
Sometime people have very little idea of the cost impact of a breaking
change. The argument is often that it will be simple to fix existing
code so that it will compile under the new version. For some people any
change to code can be very costly (those people working in critical
environments where code requires to be certified and the fact that it
compiles is not certification)
--
Note that robinton.demon.co.uk addresses are no longer valid.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
First, it would not _need_ to have either explicit or implicit, but
_can_ have either one. So, today you add "implicit" to your implicit
constructors (a great minority of total constructors). Later, in the
course of a few years, you can invoke "sed 's/explicit //'"
and "fix" the old broken code.
Second, you're speaking about explicit never being removed and not
making any difference. It _does_ make difference on the new code,
which is what we do every single day. How is it possible you
didn't even consider this when you said "makes no difference"?
>> [snip]
>>> The committee are _uniquely_ placed to be able to suggest a course of
>>> action
>>> to clean things up for the next 20-30yrs of C++ use.
>>
>> I'll just add to this... Most discussion about features that
>> someone feels should be changed, end by people stating
>> that "it would break old code". But this can only be
>> a good argument when speaking about core features, like
>> references or whatever would require unacceptable
>> amount of changes to the existing code.
>>
> With respect, that misses the point. Changing existing code is never
> cost free and sometimes the costs are much higher than just making a few
> tweaks to the codebase.
Maybe... I admit of not being competent to speak about the cost,
especially about certified code you mentioned in another reply.
But shall we analyze the principle behind the decision
of not breaking the old code?
"If we don't make it right the first time, we're stuck with it!"
And this is to be used in IT? IMNSHO, it sucks.
>> This does not conflict with my point. The changes that are coming in C++ are
>> turning it into a different language. MS found out to their horror that
>> there are an aweful lot of VC6 developers out there because none of them
>> wanted the breaking changes & uncertainty of going to VC7! (About 50% of
>> atendees at a MS developers workshop apparently)
>
> If anything this argument supports WG21 in its attempts to avoid
> breaking existing code.We spend a good deal of time trying to minimise
> the impact of changes on existing code.
VC6 is a broken C++. So people will stick with what they know instead
of learning and trying to use new features. Only half of this argument
implies what you say. The second half implies that many people will
_not_ use rvalue-references, concepts, lambdas or anything else
in C++0x because they already do it their own way. Those that will,
those are the same people that switched to VC7. OMG, when I tried VC6
it couldn't even compile it's own std::map (or was it something else)...
And finally, and this is the last argument I'll make on this
subject - it doesn't hurt to compile the old code with "gcc -std=c++98",
or whichever standard you want. I have a feeling it will be there
for quite some time. Also, gcc-4.x will be there forever.
(Alright, you have limited options if you need to mix old and new)
So, if old code never breaks - programmers that write new code suffer.
If old code breaks - use the old compiler and almost no one suffers.
Well, it might not be as simple as I put it, but I'm pretty sure it's
much closer than what you are saying... with all due respect.
--
Dragan
Nobody should have any idea what the perfect language is, as there is
no such thing. Any junior that talks about the perfect language
should be pulled up on it quick smart. We need to be very clear about
the differences between languages, and what the implications of those
differences are.
Well-known tendencies develop after a programmer uses language X a
lot. They start believing (hoping) that language X is better than
others, simply because they have invested more time into it, and are
used to it.
Saying that you "utterly despise" language Y serves no purpose until
you offer reasons for this feeling. Otherwise it just seems like (see
previous paragraph).
-Andrew.
Both Java and C# allow all the low-level features that C++ does,
because ultimately you can call C functions (and hence asm as well)
from all languages - plus C# has "unsafe" sections that quickly unlock
certain low-level but dangerous features.
The real difference with calling C from C++ is how quick and easy it
is compared to having to wrap a native interface. One has to ask the
question though, really how often do you need these low-level
features? If it is only occasionally than the distinction is
minimal. If it is often, than you should probably be working in non-
OO C.
C# has a host of generic meta-programming features, and a lot of other
language features (including many that are planned for C++0x)
available today. I encourage you to take a look through the list of
26 C# features posted here...
http://en.wikipedia.org/wiki/C_Sharp_(programming_language)
...before making the declaration that C++ is more generic than C#.
Performance is a tricky one. It is very hard to make a scientific
comparison. In my experience the largest influence on performance (in
a codebase of any reasonable size) is the asymptotic behavior of the
data structures and algorithms selected - and is not much dependent on
the language.
I will say however, that many programmers do not understand how JIT
compilation really works, or how generational garbage collection
really works. For example many incorrectly assume that virtual
machines interpret bytecode instruction-per-instruction - and hence
have an "intuition" that C++ "must" be faster.
Yes, C++ is faster than C# - as C is faster than C++ - and as asm is
faster than C. The question is how much faster is C++ than C# for
some application? 200%? 20%? 1%? 0.0001%? It is worth testing. Also,
remember that a JIT optimizer has information available at runtime
that is not available statically at compiletime. It is conceivable
that it might find optimizations that a C++ compiler cant.
We should also keep in mind that early versions of Java in particular
were very slow, and didn't take advantage of some of the modern VM
techniques we have today. If you are making an assessment based on a
10 year old version of Java, I would encourage you to retest on a
current version.
As for destructors - I assume you know about "finalize" methods that
are available in Java and C# - and when you talk about destructors, I
assume you mean the predictable order of destructor call that you get
in C++ with auto vars, reference-counted GC and by calling delete?
If so, yes, unpredictable destructor order is an inherit part of mark-
and-sweep garbage collection, and yes, you need to change your design
accordingly. Mark-and-sweep has many advantages to make the
adjustment worth your time. It is mainly realtime applications that
are negatively impacted to this. Anyway, it is usually a bad idea to
do much of anything in a destructor if you can avoid it.
The main problem I have with Java and C# is one that is not usually
raised. Using C# ties you to Windows (yes I know about Mono, it isn't
in sync), and using Java requires your customers to separately install
a bulky Java VM - forcing them into a relationship with Oracle/Sun -
and providing a barrier to experimental installation (a shareware
trial for example). This is the main reason we still develop our
production code in C++, as it allows us to be self-contained and
eliminate external dependencies (we dont even use STL) - the only
dependencies we have are system calls to the OS.
If in some alternate universe C# was completely open source, and a
standard VM shipped on every platform, it would be the preferable
target for a vast majority of applications currently written in C++.
Enjoy,
Andrew.
> Nobody should have any idea what the perfect language is, as there is
> no such thing.
Flawed reasoning. People do things striving towards perfection, but
never reach it, since perfection is by definition something that
cannot exist.
The idea exists, however.
> Any junior that talks about the perfect language
> should be pulled up on it quick smart. We need to be very clear about
> the differences between languages, and what the implications of those
> differences are.
Sounds like indoctrination. This is obviously wrong, as the best
usages of everything have been when people have went away from all the
established doctrines. There are several examples in the field of
programming, but also in the fields of economics, politics, marketing,
whatever.
Let people be free to turn any language the way they want. If you want
examples in C++, think meta-programming and expression templates
(which leads to the possibility to create Domain Specific Embedded
Languages within C++).
> Well-known tendencies develop after a programmer uses language X a
> lot. They start believing (hoping) that language X is better than
> others, simply because they have invested more time into it, and are
> used to it.
I don't really see where all of this is going.
What are you trying to prove? That I am victim to that tendency and
would be better off with C#?
> Saying that you "utterly despise" language Y serves no purpose
It serves to show not all people desire Java or C# if they don't care
about compatibility.
I don't need to justify my reasons for that.
I have a perfect idea. It would be C++, but only if it was free
from legacy constraints, free to fix bad decisions from the past.
> Well-known tendencies develop after a programmer uses language X a
> lot. They start believing (hoping) that language X is better than
> others, simply because they have invested more time into it, and are
> used to it.
>
> Saying that you "utterly despise" language Y serves no purpose until
> you offer reasons for this feeling. Otherwise it just seems like (see
> previous paragraph).
I'd like to try... I work in Java _much_more_ than in C++ and still
despise it. Here is the worst part - data structures. Like you said,
each language has its purpose. But in almost any, except some basic
scripting, you need data structures. Working with data structures
is a pain in the donkey, and this is only _after_ they introduced
generics.
You cannot declare a function returning a const object. How would you
like if someone took your cached object and invoked a mutator method?
You can protect it by returning a copy of this object. And there comes
another nonsense. One would thing that clone() is the best and elegant
solution to making copies of objects. But this is true only if all
your fields are primitive types. If you forget to deep-copy
this new HashMap-field you just added or, as frequently happens
to me, someone else adds the field and forgets to update clone(),
then have a nice day diagnosing a shiny, new, random-occurring bug.
After getting frustrated with data structures over and over again,
my current "solution" to the constness problem is to name methods like
getFooReadOnly() just to make sure users (including me after a while)
know what they get. I know - I'm silly!
So much for increased safety in Java.
To my horror, they have relatively recently marked a bug/feature request
for const as "wontfix", after quite a few years of it being open.
Oh, and need I mention that Java has no destructor... :-D
--
Dragan
> Both Java and C# allow all the low-level features that C++ does,
> because ultimately you can call C functions (and hence asm as well)
> from all languages - plus C# has "unsafe" sections that quickly unlock
> certain low-level but dangerous features.
Neither allows the control of the layout of objects in memory, which
is arguably the most important feature of C and C++ with regards to
low-levelness.
Well, you can surely allocate a big array of some primitive type and
serialize/unserialize all your data there in the layout you want, but
that just doesn't work well with the object model of those languages,
so you're basically giving up everything.
So your "Java and C# allow all the low-level features that C++ does"
is wrong.
> C# has a host of generic meta-programming features, and a lot of other
> language features (including many that are planned for C++0x)
> available today. I encourage you to take a look through the list of
> 26 C# features posted here...
>
> http://en.wikipedia.org/wiki/C_Sharp_(programming_language)
>
> ...before making the declaration that C++ is more generic than C#.
C++ allows way more than C# with regards to generic programming.
Failure to see that shows a great lack of knowledge about C++.
Also, quoting an article about C# where only 1 or 2% of the text
speaks of generics doesn't help.
Moreover, I assume you mean concepts and variadic templates by
"including many that are planned for C++0x". Those features are merely
syntactic sugar for features already present.
> Performance is a tricky one. It is very hard to make a scientific
> comparison. In my experience the largest influence on performance (in
> a codebase of any reasonable size) is the asymptotic behavior of the
> data structures and algorithms selected - and is not much dependent on
> the language.
Some design considerations of the language kind of prevents optimal
performance in some situations.
Basically, with languages where all objects are pointers to memory
handled by a garbage collector, memory space is wasted, more
dereferencing is needed, and also get poor performance simply due to
bad spatial locality, which leads to bad usage of cache etc.
This is visible whenever you handle fairly large amounts of data.
> I will say however, that many programmers do not understand how JIT
> compilation really works, or how generational garbage collection
> really works. For example many incorrectly assume that virtual
> machines interpret bytecode instruction-per-instruction - and hence
> have an "intuition" that C++ "must" be faster.
>
> Yes, C++ is faster than C# - as C is faster than C++ - and as asm is
> faster than C.
This is nonsense. Languages aren't faster than others.
If I write the same thing in a language and the same thing in another,
if one program is faster than another this is simply a matter of
quality of implementation.
The real difference in languages with regards to performance is how
efficient the code you can write in them can be. C and C++ simply
allow more than C# in that regard, since you have more control of what
actually happens.
> The question is how much faster is C++ than C# for
> some application? 200%? 20%? 1%? 0.0001%? It is worth testing. Also,
> remember that a JIT optimizer has information available at runtime
> that is not available statically at compiletime. It is conceivable
> that it might find optimizations that a C++ compiler cant.
>
> We should also keep in mind that early versions of Java in particular
> were very slow, and didn't take advantage of some of the modern VM
> techniques we have today. If you are making an assessment based on a
> 10 year old version of Java, I would encourage you to retest on a
> current version.
comp.lang.c++.moderated really isn't the place for evangelism of Java
and C#.
> As for destructors - I assume you know about "finalize" methods that
> are available in Java and C# - and when you talk about destructors, I
> assume you mean the predictable order of destructor call that you get
> in C++ with auto vars, reference-counted GC and by calling delete?
>
> If so, yes, unpredictable destructor order is an inherit part of mark-
> and-sweep garbage collection, and yes, you need to change your design
> accordingly. Mark-and-sweep has many advantages to make the
> adjustment worth your time. It is mainly realtime applications that
> are negatively impacted to this. Anyway, it is usually a bad idea to
> do much of anything in a destructor if you can avoid it.
Or rather, finalizers are unusable due to unpredictable destruction
order (another issue is also non determinism) and thus you need RAII
to handle resources properly.
And RAII in Java is a pain, and is also only slightly more practical
in C#.
> The main problem I have with Java and C# is one that is not usually
> raised. Using C# ties you to Windows (yes I know about Mono, it isn't
> in sync)
Then refrain from using the newer features not implemented by Mono.
> and using Java requires your customers to separately install
> a bulky Java VM
Because C# doesn't?
Also you can install those environments during the installation of
your own software.
You may also strip those environments bare and compile the code to
native if you wish.
> If in some alternate universe C# was completely open source, and a
> standard VM shipped on every platform, it would be the preferable
> target for a vast majority of applications currently written in C++.
Preference is people-specific. Why do you always have to state your
opinion as if it were that of everyone?
It would be preferable for you maybe, but maybe some others would
prefer sticking with C++.
I was with you till I got to the above paragraph. There are many places
that C++ compilers can generate faster code than C compilers even for
well crafted code because they have more meta-data available (an example
is usage of overloading the function operator) In addition there is the
cost of learning to write well crafted code and writing it when you know
how to do it. Many C compilers will generate code that is at least as
fast as that produced by all but the most expert of assembler
programmers and the cost in programming time is usually much smaller.
>
> We should also keep in mind that early versions of Java in particular
> were very slow, and didn't take advantage of some of the modern VM
> techniques we have today. If you are making an assessment based on a
> 10 year old version of Java, I would encourage you to retest on a
> current version.
And one particularly important metric is the over all total time spent
from specification to abandonment of the program (because it has been
superseded in some way)
>
> As for destructors - I assume you know about "finalize" methods that
> are available in Java and C# - and when you talk about destructors, I
> assume you mean the predictable order of destructor call that you get
> in C++ with auto vars, reference-counted GC and by calling delete?
No, dtors offer more than finalize methods (not least because the latter
have to be tracked by the programmer)
<snip>
> The main problem I have with Java and C# is one that is not usually
> raised. Using C# ties you to Windows (yes I know about Mono, it isn't
> in sync), and using Java requires your customers to separately install
> a bulky Java VM - forcing them into a relationship with Oracle/Sun -
> and providing a barrier to experimental installation (a shareware
> trial for example). This is the main reason we still develop our
> production code in C++, as it allows us to be self-contained and
> eliminate external dependencies (we dont even use STL) - the only
> dependencies we have are system calls to the OS.
Oh and in what way does the STL generate external dependencies. To use
C++ and not use, for example, std::vector is rather like buying an
air-conditioned house and then using wood burning heaters and hand held
fans. There is nothing wrong with the latter but why pay for air
conditioning if you do not intend to use it :-)
While I understand the reasons of the advocates I do not like using a
highly protective language (such as Pascal or Java) as a first language
because it makes it harder to teach future professionals a sense of
responsibility for their code. I also advocate that the second and third
languages learnt should be markedly different to the first so as to give
the maximum chance to break the mindset that all languages are basically
the same with relatively minor variations (like those that continue to
write Fortran in every other language the use)
And yes, I know I am a maverick, but languages such as C++, Forth and
possibly Python are excellent starters exactly because they have so much
potential for misunderstanding and make demands on the users to be
responsible for their work.
Here's what Bjarne Stroustrup has to say about it on his FAQ:
http://www.research.att.com/~bs/bs_faq.html#Java
My understanding is that a major goal of C++ is for it to be the best
language to write operation systems and the runtimes for languages
like C# and Java.
I'd allow that C# is powerful, but syntactically it hasn't made enough
of its huge advantage of not having to drag a 30-year-old language
around on a chain.
James
My point was you can manually layout memory all you want from C - and
you can call C from Java. The set of possibilities is identical, it
is just a matter of how time-consuming it is to implement. Do you
really need to layout objects in memory? In "most" cases it doesn't
matter.
> > C# has a host of generic meta-programming features, and a lot of other
> > language features (including many that are planned for C++0x)
> > available today. I encourage you to take a look through the list of
> > 26 C# features posted here...
>
> > http://en.wikipedia.org/wiki/C_Sharp_(programming_language)
>
> > ...before making the declaration that C++ is more generic than C#.
>
> C++ allows way more than C# with regards to generic programming.
> Failure to see that shows a great lack of knowledge about C++.
>
> Also, quoting an article about C# where only 1 or 2% of the text
> speaks of generics doesn't help.
>
> Moreover, I assume you mean concepts and variadic templates by
> "including many that are planned for C++0x". Those features are merely
> syntactic sugar for features already present.
Ultimately there is no well-defined definition of the difference
between generic programming and nongeneric programming. All I am
suggesting is that you compare the 26 C# features listing on that page
with the features currently available in C++ and those upcoming in C+
+0x.
> Basically, with languages where all objects are pointers to memory
> handled by a garbage collector, memory space is wasted, more
> dereferencing is needed, and also get poor performance simply due to
> bad spatial locality, which leads to bad usage of cache etc.
>
> This is visible whenever you handle fairly large amounts of data.
It is not that simple. I would hazard to guess that you are making an
intuitive assessment based on how you *think* a VM might work, rather
than actually doing oranges-vs-oranges performance testing.
> > Yes, C++ is faster than C# - as C is faster than C++ - and as asm is
> > faster than C.
>
> This is nonsense. Languages aren't faster than others.
It was said that Java and C# have time and space performance
problems. When I say faster I mean better time performance. I was
merely trying to talk in terms of the original statement.
> The real difference in languages with regards to performance is how
> efficient the code you can write in them can be. C and C++ simply
> allow more than C# in that regard, since you have more control of what
> actually happens.
Once again you can fallback to C from any of the languages when you
need that extra amount of control.
> > The main problem I have with Java and C# is one that is not usually
> > raised. Using C# ties you to Windows (yes I know about Mono, it isn't
> > in sync)
>
> Then refrain from using the newer features not implemented by Mono.
It is not practical. Very few commercial projects have chosen to make
their IP dependant on Mono.
> > and using Java requires your customers to separately install
> > a bulky Java VM
>
> Because C# doesn't?
I was referring to the fact that the .NET VM is preinstalled with
Vista and I suspect also in XP SP3.
> Also you can install those environments during the installation of
> your own software.
Its messy, it substantially increases the size of deliverable and
install-time - and it even adds the requirement of install time (as
opposed to delivering a small installerless .exe for example on
windows).
> You may also strip those environments bare and compile the code to
> native if you wish.
This is not the way there were designed, and hence this approach is
not practical.
> > If in some alternate universe C# was completely open source, and a
> > standard VM shipped on every platform, it would be the preferable
> > target for a vast majority of applications currently written in C++.
>
> Preference is people-specific. Why do you always have to state your
> opinion as if it were that of everyone? It would be preferable for you maybe, but maybe some
> others would prefer sticking with C++.
By preferable target, I mean that the majority of programmers, given
familiarity with the features of C# and C++, would select C# for most
applications (given that C# were open source, cross-platform, and OS-
preinstalled).
-Andrew.
You have missed my point. Perfection is only defined where there is
some absolute quantitative scale. The quality of a programming
language can only be measured against a specific domain - there is no
"absolute" domain of a programming language - and hence the term
"perfect language" makes no sense.
> > Saying that you "utterly despise" language Y serves no purpose
>
> It serves to show not all people desire Java or C# if they don't care
> about compatibility.
> I don't need to justify my reasons for that.
Saying you utterly despise X, is as interesting as saying "I like the
color of this tie". Nobody cares until you provide reasons. You may
as well post line noise.
-Andrew.
The CLR and JVM are written in C and do not use C++. C is really the
most chosen language to write runtime systems and operating systems.
C++ is probably the best choice for realtime embedded applications.
Mars Rover is a good example.
-Andrew.
This is completely incorrect. Any C++ program can be rewritten in C
and will compile to the same identical application. Furthermore
standard C++ classes impose restrictions on how memory is bound to
data-structures, moved, reallocated and so forth. This can be
optimized by writing the data structure in C using pointers and
functions, and not using classes. Clearly the former takes longer and
is more error prone, but the resulting code is faster than what C++
will produce.
> No, dtors offer more than finalize methods (not least because the latter
> have to be tracked by the programmer)
finalize methods are automatically called. They do not need to be
tracked by the programmer.
> Oh and in what way does the STL generate external dependencies. To use
> C++ and not use, for example, std::vector is rather like buying an
> air-conditioned house and then using wood burning heaters and hand held
> fans. There is nothing wrong with the latter but why pay for air
> conditioning if you do not intend to use it :-)
This is a common misconception. The STL is just another container and
algorithm library. Sure it is heavily used and tested, and comes
preinstalled with the C++ compiler - and for most small projects it is
fine. If you have a large codebase however you find that it is
difficult to extend and customize the data structures. If you look
inside the implementation of the STL (under gcc or MSVC for example)
we find that its internal structure and coding conventions are quite
alien. We prefer to have a consistent structure to our base
libraries, so we have simply written and integrated our own container
classes. It doesn't take as long as you think, and the performance is
significantly better because you can integrate underlying memory
management mechanisms, types and type properties that the STL is not
designed to take advantages of (or at least not easily).
-Andrew.
Furthermore
> standard C++ classes impose restrictions on how memory is bound to
> data-structures, moved, reallocated and so forth. This can be
> optimized by writing the data structure in C using pointers and
> functions, and not using classes.
:-) Would you like to provide a short example?
You still have not addressed your claim wrt external dependencies. I
have no doubt that a task specific container might sometimes prove to be
better. However do not forget to add in the cost of training newcomers
to your workshop because their code-reading skills will not port cleanly
into your environment.
> You have missed my point. Perfection is only defined where there is
> some absolute quantitative scale.
Perfection means having all qualities.
Since some qualities are mutually exclusive, perfection is not
possible.
Yet people have an image of a set of broad qualities that can be
provided at the same time; that is what they mean when they think of a
perfect <something>.
Anyway, I'm tired of discussing basic notions of language and
philosophy.
> Saying you utterly despise X, is as interesting as saying "I like the
> color of this tie". Nobody cares until you provide reasons. You may
> as well post line noise.
You made a claim (people want this...), I provided an example (no i
don't) to refute it.
There is no need to say more.
Not all features of C++ can be implemented in C. The most important
one is exceptions.
> Furthermore
> standard C++ classes impose restrictions on how memory is bound to
> data-structures, moved, reallocated and so forth. This can be
> optimized by writing the data structure in C using pointers and
> functions, and not using classes. Clearly the former takes longer and
> is more error prone, but the resulting code is faster than what C++
> will produce.
Standard containers don't impose any restrictions, other than the fact
that they're non-intrusive, that the types cannot be abstract and that
their allocation mechanism kind of sucks -- but a typical C programmer
wouldn't do better --, that prevent optimal usage.
You seem to fail at understanding generic programming. Generalizing
stuff brings zero penalty if the concepts are well crafted.
And as a matter of fact, there is none with standard containers
themselves, or at least their C++0x revision.
C++ will generate the same assembly as the equivalent C code (if it
can be made).
What exactly is a "char const (&) [N]" ? Can you write out the
signature of your suggested "member template"? And explain how is it
supposed to address the problem with character encodings?
> But this is tangential to the point, so maybe a better example is a large
> integer class with an implicit conversion from an int; repeat my same
> argument above with this.
Suppose you have a function that takes a big integer:
void MyFunction(BigInt n);
and you have a normal integer:
int x = ...;
with implicit conversion you type:
MyFunction(x);
with explicit conversion you type:
MyFunction(BigInt(x));
The generated code is identical in both cases. (If it is not, it
should be.)
The benefit of implicit conversion is saving on typing 8 characters.
Big deal.
The benefit of explicit conversion is that it is clear which overload
of MyFunction is being called, the call to the BigInt constructor is
clearly visible, and noone accidentally converts from an int to a
BigInt by accident. But mostly - it makes it clear to the compiler
and to other programmers what you want to do.
In my opinion the benefits of explicit conversion always outweigh
those of implicit conversion.
-Andrew.
> My point was you can manually layout memory all you want from C - and
> you can call C from Java. The set of possibilities is identical, it
> is just a matter of how time-consuming it is to implement.
This is a different point that the one you made earlier. You claimed
Java could do all low-level C/C++ features, and now you're saying that
you have to write those in C and use them from Java.
Personally, I don't really see the point in doing that. It's much more
advantageous to just write all the code in C++ rather than parts in
Java then parts in C when profiling shows they're a problem,
especially since interfacing languages is always going to be a pain.
If I wanted a hybrid language environment, I'd rather use Python
anyway, because it has real advantages with regards to rapid code
development and prototyping, unlike Java.
> Do you
> really need to layout objects in memory? In "most" cases it doesn't
> matter.
Matters to me.
I care about locality, cache friendliness, vectorization, and memory
usage. And I'm not especially into high-performance scientific
computing.
> Ultimately there is no well-defined definition of the difference
> between generic programming and nongeneric programming.
Sure there is.
The main formal notion behind generic programming is parametric
polymorphism. Provided by both C++ and C#, but not Java.
Note that when I said C++ allowed more, I didn't say C# didn't allow
generic programming. I mostly meant meta-programming and related
facilities, actually, that cannot be done in C# as far as I know.
> All I am
> suggesting is that you compare the 26 C# features listing on that page
> with the features currently available in C++ and those upcoming in C+
> +0x.
Only compelling feature I can see that C++ doesn't have is dynamic
duck typing as provided by C# 4.0.
Feature that couldn't come as is in C++, since it would have an
important memory overhead when you don't use those features, which is
against C++ policy.
In C#, it's not such a problem, since it's designed for JIT in mind
anyway.
> It is not that simple. I would hazard to guess that you are making an
> intuitive assessment based on how you *think* a VM might work, rather
> than actually doing oranges-vs-oranges performance testing.
It's not about how I think it works, it is how it is compelled to
work.
In C or C++, if I make an array of objects, those objects are right
after each other in memory, and that's how I can index them. In Java,
if I make an array of objects, the array actually holds pointers to
those objects right after the other, with those objects placed
somewhere on the heap.
Now, if I'm lucky, the moving collector (assuming the implementation
has one -- most Java ones don't) will eventually defragment the memory
and put all those objects right after each other in some place; but
still, I would be indexing from my array of pointers and jumping there
and back everytime. And there is no hope of vectorization, of course.
> It was said that Java and C# have time and space performance
> problems.
That's not really it. They simply compel you to write your code in a
certain way that may not be optimal. A different issue altogether.
> > Then refrain from using the newer features not implemented by Mono.
>
> It is not practical. Very few commercial projects have chosen to make
> their IP dependant on Mono.
Is that supposed to be an argument?
"Eating apples is not practical. Indeed, very few restaurants have
chosen to make their dishes dependent on apples." is not a compelling
argument not to eat apples.
Moreover, how would that make you dependent on Mono? You can run the
code on the CLR all you like; Mono is compatible with it.
Companies have no problem restricting themselves to a subset of C++
for supposedly portability reasons, why couldn't they do the same with
C#?
> > You may also strip those environments bare and compile the code to
> > native if you wish.
>
> This is not the way there were designed, and hence this approach is
> not practical.
This is certainly not a problem for an open-source implementation, and
shouldn't be that much a problem for a closed one either.
I suppose you just prefer things to be easy, that's why you want to
target Vista with its preinstalled CLR and not do proper package
installation with dependency checking.
> By preferable target, I mean that the majority of programmers, given
> familiarity with the features of C# and C++, would select C# for most
> applications (given that C# were open source, cross-platform, and OS-
> preinstalled).
And who are you to know what the majority of C++ programmers think?
For a lot of C++ programmers, I can guarantee you the set of C++
features covers more what they want than the set of C# features. Which
is partly why they're using C++ in the first place.
C++ can be quite slower than C#. E.g. io streams in C# are IMHO faster
than C++ io streams.
Generally I agree, C++ should be commonly faster.
But development in C# is IMHO faster than in C++. No need to separate
code in 2 files (header and implementation + no macros) and therefore
faster compilation and the main advantage it has an ABI, which allows to
export objects which can be used in any other (.NET) languages. Or the
other way round - all other libraries can be accessed directly, without
needing libraries wrappers.
> [...]
> If in some alternate universe C# was completely open source, and a
> standard VM shipped on every platform, it would be the preferable
> target for a vast majority of applications currently written in C++.
If there would be a native C# compiler which also supports RAII I think
I would have a new favorite language I would prefer over C++.
Currently I hope C++ modules will get it right in a few (??) years :-(
> Enjoy,
> Andrew.
Andre
> What exactly is a "char const (&) [N]" ?
A reference to an array of N constant characters. There are tools that
translate C++ types to straight english for beginners, if you're
interested.
> Can you write out the
> signature of your suggested "member template"? And explain how is it
> supposed to address the problem with character encodings?
He meant that you can use an overload to detect arrays and string
literals that can be distinguished from char*.
template<size_t N>
void f(char const (&s)[N]); // matches arrays of const char and string
literals
template<size_t N>
void f(char (&s)[N]); // matches arrays of char
void f(char* s); // matches pointers to char
void(const char* s); // matches pointers to const char
This doesn't especially help with encodings. To deal with encodings,
you should use design by contract, or eventually introduce new types
that maintain the invariant of a certain encoding.
> The benefit of explicit conversion is that it is clear which overload
> of MyFunction is being called
As I already told you, even without implicit conversions, finding
which overload is called is not necessarily clear, especially with
templates involved.
Also, in OOP, when you write obj.foo() (which is really semantically
the same as foo(obj)) you don't know which version of foo is called
either, and that is even worse, because you cannot know at compile-
time.
Yet you certainly wouldn't want to ban that behaviour, would you?
On Jun 15, 5:14 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> On 14 juin, 00:46, Andrew Tomazos <and...@tomazos.com> wrote:
>
> > You have missed my point. Perfection is only defined where there is
> > some absolute quantitative scale.
>
> Perfection means having all qualities.
> Since some qualities are mutually exclusive, perfection is not
> possible.
No. Perfect means the "best possible". This requires criteria for
comparison. You can't say one programming language is better than
another one in general. You can only say one programming language is
better than another one for some *specific* application. The better
defined the requirements are, the better defined the term "perfect
language" becomes. In isolation the term "perfect language" is
virtually meaningless. This is an important point to make, as many
assume discussion in the context of their specific application area.
> > Saying you utterly despise X, is as interesting as saying "I like the
> > color of this tie". Nobody cares until you provide reasons. You may
> > as well post line noise.
>
> You made a claim (people want this...), I provided an example (no i
> don't) to refute it.
If you read what I wrote, I said "*most* people want this". The
existence of one person that disagrees does not refute my claim.
-Andrew.
You don't understand. Java has what is called a native interface.
Clearly you have never used it. You add the "native" keyword
attribute to one of your member function declarations in Java. Then
the build system will generate a stub implementation of that function
in C. You then fill out the implementation in C. It is slightly more
painful than doing it in C++, but it is certainly easier than you
think.
> If I wanted a hybrid language environment, I'd rather use Python
> anyway, because it has real advantages with regards to rapid code
> development and prototyping, unlike Java.
Python, as a dynamically typed scripting language, does not scale
well, because binding problems are not discovered until runtime. Most
programmers agree that Python/Perl/Ruby/Lua are not in competition
with C++/Java/C# for most requirement sets.
> Matters to me. I care about locality, cache friendliness, vectorization,
and memory
> usage. And I'm not especially into high-performance scientific computing.
There are many that feel this way - however remember the old saying
"Premature optimization is the root of all evil". Sometimes it is
more important to get something stable out the door rather than
optimize for every last cycle. Usually juniors need to learn this
lesson the hard way though. :)
> Sure there is.
> The main formal notion behind generic programming is parametric
> polymorphism. Provided by both C++ and C#, but not Java.
No, Java has offered type parameters for many years (since version
5.0).
> Note that when I said C++ allowed more, I didn't say C# didn't allow
> generic programming. I mostly meant meta-programming and related
> facilities, actually, that cannot be done in C# as far as I know.
Java/C# do runtime type parameters. C++ does compiletime type
parameters. There are tradeoffs. I will grant you that C++s system
with POD template parameters, specialization and SFINAE is more
expressive and executes faster - on the other hand it does generate a
lot of codesize and slow down compilation as the extern template
keyword isnt well supported.
> > All I am
> > suggesting is that you compare the 26 C# features listing on that page
> > with the features currently available in C++ and those upcoming in C+
> > +0x.
>
> Only compelling feature I can see that C++ doesn't have is dynamic
> duck typing as provided by C# 4.0.
If you can only see one compelling feature in that list of 26 than you
need to get more experience programming in the large.
> In C or C++, if I make an array of objects, those objects are right
> after each other in memory, and that's how I can index them. In Java,
> if I make an array of objects, the array actually holds pointers to
> those objects right after the other, with those objects placed
> somewhere on the heap.
This is my point. You don't understand how it works. Go and look at
C#'s value type hierarchy and reevaluate your position.
> Companies have no problem restricting themselves to a subset of C++
> for supposedly portability reasons, why couldn't they do the same with
> C#?
> [snip]
> target Vista with its preinstalled CLR and not do proper package
> installation with dependency checking.
"Easy" is the wrong word. It is a useful project management principle
to "stay with the pack", so when something goes wrong there is a
community to support you. If you get too adventurous your foundations
falls out from underneath you.
> > By preferable target, I mean that the majority of programmers, given
> > familiarity with the features of C# and C++, would select C# for most
> > applications (given that C# were open source, cross-platform, and OS-
> > preinstalled).
>
> And who are you to know what the majority of C++ programmers think?
My extrapolation is based on 15 years professional programming
experience during which I have worked with approximately 200 different
C++ programmers. You can find more details about my background at
http://www.tomazos.com if you are curious.
-Andrew.
There are various ways to implement exceptions and of course *all* of
them can be implemented in C - in the worst case this may involve asm
blocks and/or long jumps.
You must be careful to read what I say and not strawman my position.
I never claimed that it would be *easier* to write the application in
C, simply that it is possible. The point is that everything is
possible from all languages, it is just a matter of which things are
easy and which are hard. Following from this the comparison of
languages should center on which activities are *common*, not just
which *exist*. You shouldn't say I use language X because this task
is impossible in language Y. This is almost never the case.
> Standard containers don't impose any restrictions,
> [snip]
> C++ will generate the same assembly as the equivalent C code (if it
> can be made).
That is not correct. Every feature of C++ *must* impose a
specialization of what is possible with more fundamental building
blocks (the long way). These are the "restrictions".
For example, the way that C++ implements constructor chaining, or
virtual method dispatching, or the memory layout of classes, is *not*
the only way it can be done - and therefore *cannot* be optimal for
*every* application.
This is a simple and straightforward point.
-Andrew.
The reason most people use a given language is rarely based on an
objective evaluation of the alternatives. Usually language is:
(1) past down from a teacher to a student (I learned language X at
school, uni or on-the-job)
(2) packaged with existing code (It's already written in X, so I have
to use it because I cant afford to rewrite it)
(3) prior programming experience (I know language X really well, I
don't want to have to learn the nuances of some other language Y)
In any case, I disagree with your statement regarding the relative
feature coverage of C++ and C# with regard to what most of today's C++
programmers would want for most of their current application domains.
Let's put the "microcomparison" aside for a moment and think about
this from a big picture view:
Anders Hejlsberg and his team have the freedom (beyond the closed
source and platform issues discussed previously), qualification,
financial backing and goal to deliver a programming language that
competes with C++ in most domains *without* the legacy considerations
that C++ has to drag behind it.
What you are suggesting is that despite this clear strategic advantage
over C++ (and with the benefit of C++ in hindsight) they have not been
successful in their goal. If this were the case it should be quite
surprising shouldn't it?
-Andrew.
>
> That is not correct. Every feature of C++ *must* impose a
> specialization of what is possible with more fundamental building
> blocks (the long way). These are the "restrictions".
>
> For example, the way that C++ implements constructor chaining, or
> virtual method dispatching, or the memory layout of classes, is *not*
> the only way it can be done - and therefore *cannot* be optimal for
> *every* application.
>
> This is a simple and straightforward point.
It maybe but none-the-less your conclusion is wrong. Modern optimising
compilers make extensive use of meta-data. This sometimes means that a
higher abstraction level does result in the compiler being able to
generate more efficient code.
One point often forgotten is that our programs do not operate in uniform
memory but multiple levels of caching and backing storage. For example
it is often asserted that a binary search will run faster than a linear
one. All things being equal, that would be true but the assumptions
about locality etc. are ignored in that statement. If the whole data
fits into the top level cache a simplistic search will often beat a more
advanced one (the latter often stresses the cache to much and results in
paging faults.)
The days when the best optimising compiler was a factor of two or more
worse than the best hand written code are long gone. I expect an
optimising compiler to beat anything I can write (and I come from a
background where you counted cpu cycles when writing assembler in order
to get best performance)
I think you are falling into the trap of assuming that what seems
obvious (re optimisation) will also be correct. All the evidence is that
humans are very bad at assessing that, whether it be within a language
or between languages.
This is completely irrelevant. We were discussing using implicit
conversion versus explicit conversion. You will agree that requiring
explicit conversion will make it clearer than implicit conversion
which overload is called. That's the point.
> Also, in OOP, when you write obj.foo() (which is really semantically
> the same as foo(obj)) you don't know which version of foo is called
> either, and that is even worse, because you cannot know at compile-
> time.
>
> Yet you certainly wouldn't want to ban that behaviour, would you?
Of course not. Virtual method dispatch has nothing to do with
explicit vs implicit type conversion.
Ive listed good reasons to use explicit type conversion. The only
reason I can see for using implicit conversion is to save on typing.
The tradeoff seems obvious.
-Andrew.
That was not what I meant. Any C++ application can be rewritten in C
such that it will generate an identical application (ie the same
object code). When you think about it, this is trivially true, since
ultimately you can just put an asm block in the main function and type
out the entire program verbatim.
The point is the C++ compiler doesn't have any magic power to generate
faster code than C. The "meta-data" system you speak of (virtual
methods, inheritance, constructor chaining, RTTI, etc) can all be
implemented in C. Note that many scripting languages (that do way
more wacky things than the C++ compiler) are mostly implemented in C.
Go and study the C techniques that the Ruby interpreter...
ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p129.tar.gz
...uses if you want to see such things in action.
> > standard C++ classes impose restrictions on how memory is bound to
> > data-structures, moved, reallocated and so forth. This can be
> > optimized by writing the data structure in C using pointers and
> > functions, and not using classes.
>
> :-) Would you like to provide a short example?
You wouldn't need an example if you understood the simple underlying
concept that structs and functions are more *fundamental* building
blocks than classes. This is why structs/functions must be more
flexible, and have more opportunity for optimization. C++ classes are
essentially a special case of structs/functions - another higher
"layer" above them. It is clearly possible to implement, customize
and optimize that higher layer in C.
Classes cannot possibly do things that can't be achieved with structs
and functions. The C++ compiler itself essentially uses structs/
functions to implement classes.
If you want an example of how C++ classes provide restrictions, than
take a look at realloc and move constructors. Sometimes objects can
be moved (not copied) in memory more efficiently than by calling the
copy constructor and destructor. In fact, sometimes objects can be
moved without calling anything at all. In these cases an array of
those objects can be moved with block memory operations, or
reallocated with realloc. Using the standard C++ practices, as
demonstrated in the STL for example, does not allow for this
optimization. This is one example of many.
A general-purpose solution is forced to makes tradeoffs that a more
specialized solution isn't.
> You still have not addressed your claim wrt external dependencies.
What claim? It was a definition, not a claim. We call the STL an
external dependency to our codebase. The STL was not written by us,
so we call it "external". If we use the STL from our codebase, than
the smooth functioning of our code "depends" on the STL. Hence by our
definition the STL is an "external dependency".
> I have no doubt that a task specific container might sometimes prove to be
> better. However do not forget to add in the cost of training newcomers
> to your workshop because their code-reading skills will not port cleanly
> into your environment.
Firstly, have you tried reading the STL code? The interface is all
very nice and orthogonal, but its implementation is shockingly obscure
and seems unnecessarily indirect and aliased. I would not be happy if
someone turned in code like that as a work product. It all works
solidly because of thorough testing-thru-use - but what happens when
you want to make changes to it? It would be hard to refactor.
Secondly, as the size of the codebase grows the benefit of prior STL
experience goes to zero (ie the STL is just a tiny piece of a large
codebase).
Finally, as we use the codebase-wide coding conventions in our inhouse
"STL replacement", the code is more readable to our team. New
programmers only have to learn our conventions once, and then they can
easily read the whole codebase.
This attitude is quite common in large C++ frameworks. Many of them
have do not use some or all of the STL in favor of their own
implementation.
-Andrew.
[snip]
> If there would be a native C# compiler which also supports RAII I think
> I would have a new favorite language I would prefer over C++.
If I find a language that's value-based, with support for
metaprogramming, generic programming and OO. And without the C quirks,
I would have a new favorite.
[snip]
{ sig & banner removed. don't quote extraneous material. tia., -mod }
> > As I already told you, even without implicit conversions, finding
> > which overload is called is not necessarily clear, especially with
> > templates involved.
>
> This is completely irrelevant. We were discussing using implicit
> conversion versus explicit conversion. You will agree that requiring
> explicit conversion will make it clearer than implicit conversion
> which overload is called. That's the point.
I fail to see how it is irrelevant.
The (only) argument against implicit conversions you gave is that it
makes it difficult to tell which overload is called.
Banning implicit conversions does not make that any clearer, however,
due to other facts, rendering your argument moot.
>
> > Also, in OOP, when you write obj.foo() (which is really semantically
> > the same as foo(obj)) you don't know which version of foo is called
> > either, and that is even worse, because you cannot know at compile-
> > time.
>
> > Yet you certainly wouldn't want to ban that behaviour, would you?
>
> Of course not. Virtual method dispatch has nothing to do with
> explicit vs implicit type conversion.
Yet it is the same as overload resolution except the resolution is
resolved at runtime.
The reason why you want to get rid of implicit conversions is because
it makes which overload is called unclear. With virtual funciton
dispatch, not only is that unclear, you can't even statically compute
which one is called.
Keep in mind also there are other implicit conversions scenarios than
implicit constructors and conversion operators: conversion to a base
type and to a cv-qualified version of the same type.
> That was not what I meant. Any C++ application can be rewritten in C
> such that it will generate an identical application (ie the same
> object code). When you think about it, this is trivially true, since
> ultimately you can just put an asm block in the main function and type
> out the entire program verbatim.
But that's not C anymore, then.
The asm keyword has unspecified meaning as far as standard C is
concerned (same for C++).
> The point is the C++ compiler doesn't have any magic power to generate
> faster code than C.
Yes, it does, as I already stated: exceptions.
There are much faster than branching and return codes that a C program
would have to do. The recommended implementation has zero (or near
zero, depending on which one you consider recommended) overhead in the
case where no exception is thrown.
> If there would be a native C# compiler which also supports RAII I think
> I would have a new favorite language I would prefer over C++.
It is by nature not a really good idea to natively compile C#, since a
lot of features requires the compiler to have knowledge of the current
program at runtime.
OK so we start with a C++ program, get a high quality optimising
compiler to generate object code then take that object code and insert
it into a C program (note that C does not have an asm keyword, unlike
C++) and somehow ensure that the result is compiled with a C compiler.
Well all I will add is that the result is not a C program because by
your own admission the bulk of it is not written in C.
Anyway this discussion is going nowhere. You make assertions based on
expectations and Andrew Koenig and others have made contrary assertions
based on experiment. I will stick with the latter. If you want you can
have the last word because I have nothing more to add.
CLR/CLI VM is written in C++. The HotSpot Java VM too.
JP
"Design by contract" is just buzzword fluff. The solution to
encodings is simple. You require explicit declaration of the encoding
whenever string data enters or leaves the system:
// pseudo-code
char* p_in = ...; // data comes in
import_this_string(p_in, this_is_utf8);
...
char* p_out; // data goes out
export_this_string(p_out, write_as_utf8);
and you make it statically illegal to narrow to an inappropriate
encoding (utf8 -> latin1, latin1 -> ascii, etc).
There are several specific ways to implement this. The way we do it
is to have a different string type for every encoding - with explicit
conversion between them only for broadening conversions.
Roughly:
class Ascii { ... };
class Latin1 { Latin1(Ascii); ... }
class UTF8 { UTF8(Latin1); UTF8(Ascii); ... }
class UTF16 { UTF16(UTF8); UTF16(Latin1); UTF16(Ascii); operator
UTF8(); ... }
etc // for nits: public, explicit and const& omitted for brevity
So the above example would look something like:
char* p_in = ...;
UTF8 my_in_string(p_in);
...
UTF8 my_out_string = ...;
char* p_out = my_out_string.ptr();
// for nits: you need to take a copy of the
// memory and not keep pointer to internal string buffer
Enjoy,
Andrew.
Noone is suggesting we throw out compilers and start writing code by
hand. We are talking about two different things. If you use C++
classes, you must agree that they automatically impose some structure
on the code they generate. You must also agree that this structure is
not always optimal for every problem - therefore it must be at least
*possible* for a programmer to create a better structure by hand.
Note that this is a very different statement from saying that this is
easy to accomplish.
-Andrew.
> Anders Hejlsberg and his team have the freedom (beyond the closed
> source and platform issues discussed previously), qualification,
> financial backing and goal to deliver a programming language that
> competes with C++ in most domains *without* the legacy considerations
> that C++ has to drag behind it.
>
> What you are suggesting is that despite this clear strategic advantage
> over C++ (and with the benefit of C++ in hindsight) they have not been
> successful in their goal. If this were the case it should be quite
> surprising shouldn't it?
C# is not C++ without legacy considerations.
It's a whole different language altogether, with different ways to
think, rationalize, design and program.
Yes, a lot of people prefer C# over C++, and that's why they moved.
Among those that didn't, some didn't because they couldn't, some
because they prefer C++.
Anyway, that has nothing to do with the fact that C# is successful or
not. That it is very popular is undeniable.
My original point is that while C# certainly is a good language, it
doesn't have all the qualities that C++ has. So if we can abstract
legacy considerations, C# is not necessarily what a C++ programmer
would want (as a matter of fact, I wouldn't).
1. I have a problem
2. I design a solution
3. I code it in C++, which means functions, structures, classes
(abstract, wrappers, functors, iterators, helpers)
4. Sometime in the future I consider refactoring
What is there to additionally do "by hand" and how will it make
the code better?
Even more, what is the recent discussion in this thread all about?
It seems fairly active, though...
--
Dragan
I agree that some features heavily rely on dynamic code generation.
Anyways I think it would be possible, with many other downsides.
Would be comparable to embedding a C++ compiler in an application and
ship it to dynamically bind to generic code and potentially compile and
execute C++ scripts and a disassembler/enough meta data to inject code
if needed.
But I agree, would be not a good idea. What could perhaps be (easier)
done is to embed the core runtime and ship it directly with the
application. Mono IIRC supports such a code / core bundling.
The problem (sometimes) is not native code at all, but the dependency on
a large framework.
What I really meant with native C# compiler + RAII is, rather a C++++
language. which doesn't rely on old C relics (header, macros etc., slow
compilation), but still can compile/link to "old" C++ code - if needed.
Andre
Ok, I concede that (at least the HotSpot Java VM which I just
checked). A few years ago I built Sun's Java VM for ARM and I'm
pretty sure it was written in C. I assumed incorrectly this was also
the case for the CLR. Most of the runtimes for the dynamic languages
I've seen are written in C at least.
-Andrew.
Of course it makes it clearer which one is called:
void MyFunction(A a);
void MyFunction(B b);
C c = ...;
MyFunction(c); // duh? which one is called?
MyFunction(A(c)); // oh that one
Your argument that we should ignore the advantage in this specific
case because there are other different cases where we can't do it (in
cases of virtual method call or templates etc) - is just ludicrous.
-Andrew.
I have already written my argument in another post. Yes, you're right,
but only if you name your function "MyFunction" and have obscure types
"A", "B" and "C". But this is _not_ a good example. It's exactly
like saying C++ sucks because i can write "*(int*)0 = 10".
In a good design, "A" might be a shape, "B" might be a text...
And the documentation will say that "MyFunction" draws shapes and texts,
so you need not worry which one is called, you should focus only
on what you are trying to accomplish - draw your stuff.
And if you _must_ worry, well if you don't know whether your "C"
can be converted to a shape or a text - fix the ambiguity, how
did you end up with a class that can be both shape and text?
Are those two related with text being convertible to a shape?
In this case, it means there is a better algorithm for texts,
but if you ask me, I would give different names to those functions.
What I'm trying to say - there are many choices, none of which
is influenced by implicit conversion.
In your example featuring BigInt, you shouldn't have made it
implicitly convertible from int. Why did you do it if you
want explicitness? Don't you see that it is not a good example either?
You can only argue with the writer of BigInt, not with a feature of C++.
In a good design, things _are_ clean. You must not blame C++
for bad designs. Implicit conversion is only a tool, and a nice
one, too. I would even like to see implicit conversions with
transitive properties - this would help my wrappers to
class hierarchies :-D. How would you use shared_ptr without
implicit conversions? Up-cast on every function call?
> Your argument that we should ignore the advantage in this specific
> case because there are other different cases where we can't do it (in
> cases of virtual method call or templates etc) - is just ludicrous.
Are you saying "the advantage of removing implicit conversions"?
Yet, your argument is that we should ignore the advantage
of implicit conversions because of this specific case of _bad_design_.
--
Dragan
I have already responded to the "this should never happen in a good
design" argument. See my post in this thread at [Tue, 2 Jun 2009
01:48:10 CST].
> , "A" might be a shape, "B" might be a text...
> And the documentation will say that "MyFunction" draws shapes and texts,
> so you need not worry which one is called, you should focus only
> on what you are trying to accomplish - draw your stuff.
So just write Render(Text(x)) instead of Render(x), just in case
someone doesn't realize that x can be converted to a Text and not a
Shape.
> And if you _must_ worry, well if you don't know whether your "C"
> can be converted to a shape or a text - fix the ambiguity, how
> did you end up with a class that can be both shape and text?
In my example only A has an implicit constructor from type C. The
point is without looking up the constructors of A and B you can't tell
that from seeing { C c; f(c); }
> What I'm trying to say - there are many choices, none of which
> is influenced by implicit conversion.
I don't understand. So you are saying there is never a case in a good
design where you have an overloaded context of two different types
where an implicit conversion is used to select between them?
> In your example featuring BigInt, you shouldn't have made it
> implicitly convertible from int. Why did you do it if you
> want explicitness? Don't you see that it is not a good example either?
> You can only argue with the writer of BigInt, not with a feature of C++.
The example of using a large integer implicit constructor was
suggested by "blarg" in this thread on [Mon, 8 Jun 2009 17:10:47 CST]
as an example of where implicit conversion should be used instead of
explicit conversion.
I agree with you that it is not a good example of why implicit
conversion is better than explicit conversion. In fact, my position
is that no such good example exists.
Please provide an example where implicit conversion should be used
instead of explicit conversion - to support your argument for the
existence of implicit constructors. I have never seen one.
> In a good design, things _are_ clean. You must not blame C++
> for bad designs. Implicit conversion is only a tool, and a nice
> one, too. I would even like to see implicit conversions with
> transitive properties - this would help my wrappers to
> class hierarchies :-D. How would you use shared_ptr without
> implicit conversions? Up-cast on every function call?
Please write out the relevant signatures, constructors and function
call of your example in support of implicit constructors - and I would
be happy to address it.
For now I'll assume you mean:
class B : shareable { ... }
class D : B { ... }
template <class S> class shared_ptr
{
template <class T> shared_ptr(shared_ptr<T>);
}
//for nits: -public for brevity
void f(shared_ptr<B>);
shared_ptr<D> d = ...;
With implicit conversion:
f(d);
With explicit conversion:
f(shared_ptr<B>(d));
Is that what you mean?
If it is, than first, as shared_ptrs are used all throughout the
codebase (I'm assuming) than let me introduce this:
#define DeclShareable(Type) class Type; typedef shared_ptr<Type>
SP_##Type;
// this goes ahead of any shareable subclass
DeclShareable(B);
DeclShareable(D);
Then the explicit version of the call to f becomes:
f(SP_B(d));
Yes you had to type 6 extra characters, but at least the cast is
clearly visible. I think in this specific case we have become
accustomed to the implicit conversions between native types (which I
am also against by the way) - and also the difference in safety
between explicit casts allowing narrowing, and implicit casts not.
Really what I am suggesting is that this be tightened another level.
Implicit cast should be promoted to the current explicit cast syntax -
and maybe unsafe narrowing casts should be promoted to some new
syntax. Maybe "narrow_cast<X> (y)" for example. Alternatively,
narrowing casts could just be merged in with normal casts now that
they are all explicit.
In any case, yet again, the only argument for implicit conversion is
to save on typing out "TypeName(...)".
> > Your argument that we should ignore the advantage in this specific
> > case because there are other different cases where we can't do it (in
> > cases of virtual method call or templates etc) - is just ludicrous.
>
> Are you saying "the advantage of removing implicit conversions"?
> Yet, your argument is that we should ignore the advantage
> of implicit conversions because of this specific case of _bad_design_.
Ok, so lets discuss cases you consider good design where implicit
conversion is used.
-Andrew.
--
So you have... I apologize for overlooking... Anyway, I prefer
the C++ approach. I've seen so many horrible and damaging mistakes,
overlooks, ignorance and nonsense from Java developers so that
it doesn't matter if it's a "more controlled environment".
In my experience, it comes to this: "Learn your stuff, man!".
Java developers usually think that any code they write will
magically do what they wanted. So they will shallow copy
a field they just added to my class leaving an object in cache
available for corruption. I know generalizations are bad,
but it is the consequence of "no need to worry about memory
management", "it's harder to make mistakes"...
No it's not! You don't know your language!
So... blame the developer and not the feature. Improve the feature.
Since there already exist explicit conversions, I dub this feature
fairly improved. :-)
>> , "A" might be a shape, "B" might be a text...
>> And the documentation will say that "MyFunction" draws shapes and texts,
>> so you need not worry which one is called, you should focus only
>> on what you are trying to accomplish - draw your stuff.
>
> So just write Render(Text(x)) instead of Render(x), just in case
> someone doesn't realize that x can be converted to a Text and not a
> Shape.
Well, I'll respect your coding style if you respect mine, in which
you will never have to ponder which function gets invoked.
>> And if you _must_ worry, well if you don't know whether your "C"
>> can be converted to a shape or a text - fix the ambiguity, how
>> did you end up with a class that can be both shape and text?
>
> In my example only A has an implicit constructor from type C. The
> point is without looking up the constructors of A and B you can't tell
> that from seeing { C c; f(c); }
>
>> What I'm trying to say - there are many choices, none of which
>> is influenced by implicit conversion.
>
> I don't understand. So you are saying there is never a case in a good
> design where you have an overloaded context of two different types
> where an implicit conversion is used to select between them?
It shouldn't make the user wonder what is going on. I expect if I have
a function and an argument - the function shall clearly state what
it does.
Overloads are there to have one function that covers a variety of
arguments. So why not argue that overloading is evil.
Your f(A(c)) becomes f_A(c)
Your f(B(c)) becomes f_B(c)
The two approaches are equivalent - you gain knowledge of which
function is invoked, you have to type more.
> Please provide an example where implicit conversion should be used
> instead of explicit conversion - to support your argument for the
> existence of implicit constructors. I have never seen one.
You are correct. I have given no example.
typedef std::shared_ptr<Base> Ptr_Base;
typedef std::shared_ptr<Base const> Ptr_Const_Base
typedef std::shared_ptr<Deriv> Ptr_Deriv;
typedef std::shared_ptr<Deriv const> Ptr_Const_Deriv;
Ptr_Deriv clone(const Ptr_Const_Deriv & arg);
Ptr_Deriv arg = /* init */
I _could_ write one of:
Ptr_Const_Base res =
Ptr_Const_Base(clone(Ptr_Const_Deriv(x)));
Ptr_Const_Base res =
static_cast<Ptr_Const_Base>(func(static_cast<Ptr_Const_Deriv>(x)));
... but I prefer the currently available:
Ptr_Const_Base res = func(src);
... in which I concentrate on "src", "res", "clone", and their meaning
in _my_ code, instead of typing and casting to satisfy the code someone
else wrote. I already know what types are in play, the purpose
of the function being invoked - and now I want to use them...
Another example:
Ptr_Composite compose(Ptr_Base a, Ptr_Base b);
Ptr_Base res = Ptr_Base(compose(Ptr_Base(x), Ptr_Base(y)));
... and it's clear _which_ function gets invoked. But only if you
manage to find out what exactly is going on there between cast.
In both examples, you cannot make a mistake if implicit casts
are involved. There is no gain in eliminating them, but there
is a loss of readability and too much worrying about
concrete types involved.
And now comes a change in interface:
Ptr_Composite compose(Ptr_Const_Base a, Ptr_Const_Base b);
... this change doesn't break the existing code. The developer
has decided that he/she was unnecessary required mutable
objects. Of course, the functionality remains the same.
I hope all this makes sense to at least one person besides me.
My fetish is that I want my custom types that are wrapping all
those shared_ptrs to have a transitional implicit conversion from
a "wrapper to Deriv" to a "wrapper to const Base".
Wait! Ignore this last paragraph... :-D
--
Dragan
Yes, this is true. On average Java developers are less experienced
than C++ developers. Java developers get paid about 10% less on
average too.
As people tend to stick to the language they know, this is a function
of how long the two languages have been around. ie C++ guys are
simply older than Java guys on average.
It is also easier to get something working in Java for a less skillful
programmer. A certain logical error may cause a memory overwrite in C
++ - whereas the same error in Java only causes a memory leak.
A cunning recruiter may spot the first reason and hire into C++. Not
because it is a better language, simply because people that use the
language are better. :) It's a strange world.
> Overloads are there to have one function that covers a variety of
> arguments. So why not argue that overloading is evil.
>
> Your f(A(c)) becomes f_A(c)
> Your f(B(c)) becomes f_B(c)
>
> The two approaches are equivalent - you gain knowledge of which
> function is invoked, you have to type more.
You are strawmaning my position. Overloading is not the same as
implicit vs explicit constructors. Specifically overloading has other
uses such as in constructors, operators and templates. It is
reasonable that the same function has different implementations
depending on the type of the parameters. That is not what my position
is.
My position is that explicit construction should always be prefered to
implicit construction.
> > Please provide an example where implicit conversion should be used
> > instead of explicit conversion - to support your argument for the
> > existence of implicit constructors. I have never seen one.
>
> You are correct. I have given no example.
>
> [snip example]
>
> I hope all this makes sense to at least one person besides me.
Ok, so lets take a look at your three examples:
> Ptr_Const_Base res =
> Ptr_Const_Base(clone(Ptr_Const_Deriv(x)));
> Ptr_Const_Base res =
> static_cast<Ptr_Const_Base>(func(static_cast<Ptr_Const_Deriv>(x)));
> Ptr_Base res = Ptr_Base(compose(Ptr_Base(x), Ptr_Base(y)));
The type prefix "Ptr_Const_" and "Ptr_" are very long. Even in the
largest codebases the number of type prefixes is small, and they come
up so often so it is worth introducing a shorter syntax, we will use
"P_" and "PC_". (Note further than most types dont have prefixes, so
your examples are the exception, not the rule).
Further static_cast is redundant in my suggested method, and the "T a
= T(...);" construction form is also redundant. Construction of "T a
= ..." is considered an explicit context (just another form of direct
initialization).
So your examples become:
> PC_Base res = clone( PC_Deriv(x) );
> PC_Base res = func( PC_Deriv(x) );
> P_Base res = compose( P_Base(x), P_Base(y) );
I content that it is easier to see what is going on in these explicit
examples than it is in their implicit versions.
(P.S. Type names should generally be short. If the namespace is
getting too cluttered, than partition it into several smaller
namespaces. That is what they are there for.)
-Andrew.
--
>> Java developers usually think that any code they write will
>> magically do what they wanted.
>
> Yes, this is true. On average Java developers are less experienced
> than C++ developers. Java developers get paid about 10% less on
> average too.
I think that the above is pure speculation.
>
> As people tend to stick to the language they know, this is a function
> of how long the two languages have been around. ie C++ guys are
> simply older than Java guys on average.
Actually the best programmers use multiple languages (and ten years ago
when a, now defunct, developers magazine surveyed its readership it
found that on average programmers who used multiple languages earned
about 2.5 times as much as those who were mono-lingual in their work.
No idea if this is still true today)
>
> It is also easier to get something working in Java for a less skillful
> programmer. A certain logical error may cause a memory overwrite in C
> ++ - whereas the same error in Java only causes a memory leak.
That does not look right to me.
>
> A cunning recruiter may spot the first reason and hire into C++. Not
> because it is a better language, simply because people that use the
> language are better. :) It's a strange world.
Again, pure speculation.
>
>
>> Overloads are there to have one function that covers a variety of
>> arguments. So why not argue that overloading is evil.
>>
>> Your f(A(c)) becomes f_A(c)
>> Your f(B(c)) becomes f_B(c)
>>
>> The two approaches are equivalent - you gain knowledge of which
>> function is invoked, you have to type more.
>
> You are strawmaning my position. Overloading is not the same as
> implicit vs explicit constructors. Specifically overloading has other
> uses such as in constructors, operators and templates. It is
> reasonable that the same function has different implementations
> depending on the type of the parameters. That is not what my position
> is.
>
> My position is that explicit construction should always be prefered to
> implicit construction.
Well isn't it? If I construct explicitly the compiler must do what I ask
for. Perhaps you meant something else.
>
>>> Please provide an example where implicit conversion should be used
>>> instead of explicit conversion - to support your argument for the
>>> existence of implicit constructors. I have never seen one.
>> You are correct. I have given no example.
>>
>> [snip example]
>>
>> I hope all this makes sense to at least one person besides me.
>
> Ok, so lets take a look at your three examples:
>
>> Ptr_Const_Base res =
>> Ptr_Const_Base(clone(Ptr_Const_Deriv(x)));
>
>> Ptr_Const_Base res =
>> static_cast<Ptr_Const_Base>(func(static_cast<Ptr_Const_Deriv>(x)));
>
>> Ptr_Base res = Ptr_Base(compose(Ptr_Base(x), Ptr_Base(y)));
>
> The type prefix "Ptr_Const_" and "Ptr_" are very long. Even in the
> largest codebases the number of type prefixes is small, and they come
> up so often so it is worth introducing a shorter syntax, we will use
> "P_" and "PC_". (Note further than most types dont have prefixes, so
> your examples are the exception, not the rule).
That is purely a style issue and here the more verbose style avoids
misunderstandings.
>
> Further static_cast is redundant in my suggested method, and the "T a
> = T(...);" construction form is also redundant. Construction of "T a
> = ..." is considered an explicit context (just another form of direct
> initialization).
Unfortunately WG21 felt that it could not make that change. I cannot
remember all the detail but it wasn't laziness or an arbitrary decision.
Instead we provide a new universal initialisation syntax that we are
trying to ensure is usable in alll places where initialisation takes place.
Lets take another example.
Assuming we have these classes and a function:
class Text { /* some members */ };
class ColorText : public Text { /* some members */ };
void Render(const Text&);
And we have two objects:
Text t;
ColorText ct;
Which of the sets of calls to Render would you prefer and why?
/* Set A */
Render(t);
Render(ct);
/* Set B */
Render(t);
Render(Text(ct));
/* Set C */
Render(Text(t));
Render(Text(ct));
> -Andrew.
>
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
> Ok, so lets take a look at your three examples:
>
> > Ptr_Const_Base res =
> > Ptr_Const_Base(clone(Ptr_Const_Deriv(x)));
> > Ptr_Const_Base res =
> > static_cast<Ptr_Const_Base>(func(static_cast<Ptr_Const_Deriv>(x)));
> > Ptr_Base res = Ptr_Base(compose(Ptr_Base(x), Ptr_Base(y)));
>
> The type prefix "Ptr_Const_" and "Ptr_" are very long. Even in the
> largest codebases the number of type prefixes is small, and they come
> up so often so it is worth introducing a shorter syntax, we will use
> "P_" and "PC_". (Note further than most types dont have prefixes, so
> your examples are the exception, not the rule).
When you're implementing certain types, such as views, iterators or
something behaving like a pointer, you may need to implement a const
version and a non-const version, with the non-const version implicitly
convertible to the const version, so as to mimic the T* -> T const*
implicit conversion done by the language (you still haven't said
whether you wanted to ban that one too).
An obvious example is std::vector<int>::iterator which is convertible
to std::vector<int>::const_iterator.
If you're finding Ptr_Const_Base to be a long type name, you really
are far from the realities. You have even longer names with
std::vector<int>::const_reverse_iterator.
Also, reducing it to something as short as P_ and PC_ only makes them
hard to understand to me. I don't want some kind of name mangling
inside the code itself...
In other situations, you want to use implicit conversions to mimic the
Derived* -> Base* implicit conversion done by the language (you still
haven't said whether you wanted to ban that one either). The code
given above actually used both kinds as an example of how implicit
constructors allowed to implement types that had the same proprieties
as pointers.
[ ... ]
> Any C++ application can be rewritten in C
> such that it will generate an identical application (ie the same
> object code).
This is sort true in a purely theoretical sense, but utterly false in
from any practical viewpoint.
Just for one simple example, consider the difference between qsort
from the C library and std::sort from the C++ library. qsort uses a
pointer to a comparison function, that gets called through that
pointer when a comparison needs to be done.
std::sort is a template, so the comparison you specify can and often
will be generated inline. The result is that for sorting simple
types, std::sort is _typically_ about three times as fast as qsort.
Now, from a theoretical perspective, you're sort of right: if
somebody didn't mind re-writing a quick sort (or intro-sort, etc.)
and tailoring it for each type of object that they were going to
sort, a C version _could_ be written that stood some chance of being
as fast as a C++ version.
The fact is, however, that virtually nobody ever even considers doing
such a thing -- if they're writing C and need to sort something, they
don't even think twice about using qsort. If they're writing C++,
they use std::sort, and by knowing only a few basic rules of thumb
about how to write C++, they get substantially better performance.
> When you think about it, this is trivially true, since
> ultimately you can just put an asm block in the main function and type
> out the entire program verbatim.
Trivially true _on a purely theoretical basis_! The problem with this
theory is that it treats the language as a limiting factor, and cost
as irrelevant. In reality, virtually nobody even cares about
producing the fastest code possible in a given language -- rather,
they care about producing the best code (where speed is a relatively
minor one of many factors) for the lowest cost.
> The point is the C++ compiler doesn't have any magic power to generate
> faster code than C. The "meta-data" system you speak of (virtual
> methods, inheritance, constructor chaining, RTTI, etc) can all be
> implemented in C.
There's no magic involved, but templates (in particular) give a C++
programmer a _reasonable_ ability to do things that really are not
reasonable in C at all. There are really only two ways to postulate C
being competitive: either postulate a C compiler with drastically
better optimization than any that currently exists, or postulate that
the cost of writing the C source code is never any object at all.
Both these arguments strike me as wildly unrealistic at best.
--
Later,
Jerry.
[ ... ]
> For example, the way that C++ implements constructor chaining, or
> virtual method dispatching, or the memory layout of classes, is *not*
> the only way it can be done - and therefore *cannot* be optimal for
> *every* application.
What (do you think) is the way that C++ "implements...virtual method
dispatching, or memory layout of classes"?
The reality is that C++ only requires that there BE virtual method
dispatching, NOT a thing about how it is done. The _most_ restrictive
rule about class layout in C++ is that for certain classes, the rules
are the same as for C. For all other classes, the rules of C++ are
_less_ restrictive, not more so.
It's true that most (quite possibly all) C++ compilers implement
virtual methods in essentially the same way (at least for single
inheritance). You've postulated that this can't always be optimal,
but made only an unsupported assertion that this can't always be
optimal. You've also failed to show that whatever other
implementation you think might (at least ocassionally) be superior
can't possibly be used in a conforming implementation of C++.
[ ... ]
> Noone is suggesting we throw out compilers and start writing code by
> hand. We are talking about two different things. If you use C++
> classes, you must agree that they automatically impose some structure
> on the code they generate.
What structure do you imagine C++ classes impose on the "code they
generate" that isn't imposed by C compilers or even assemblers? The
reality is rather the opposite: a C++ compiler has looser rules than
a C compiler, and a C compiler has looser rules than an assembler...
--
Later,
Jerry.
Even if it is a speculation, they still think that code will magically
do what they wanted... Of course, I don't mean _all_ developers,
It's bad to even state that it's a majority. But I'm sure it's more
than in many other languages (C++, Python). And this is a bad thing
unless they start practicing another language or take a better approach.
[snip]
>>
>> It is also easier to get something working in Java for a less skillful
>> programmer. A certain logical error may cause a memory overwrite in C
>> ++ - whereas the same error in Java only causes a memory leak.
>
> That does not look right to me.
I'll share my impression and experience. One starts coding in Java and
learns that he has on his disposal: memory management, a _very_ rich
library, threads and synchronization made easy, and you have many
examples and tutorials demonstrating how it's all simply done with Java.
Only after diving in into some _serious_ work and big projects,
you have to get back to the fundamentals and really learn the stuff.
Then you start missing features of other languages. You cannot
mix Java and C++ - it's far, far from being practical. OTOH, I like
the combination C++/Python (thumbs up to boost::python,
it's an extraordinary piece of work!).
My guess is that Andrew was saying that you need less understanding
of what is going on to write a basic functionality in Java.
Example: startup a thread to handle a connection. Easy in Java.
Also easy in C++ with boost, but I have no idea how many C++ developers
use boost. Also, boost wasn't around when I wrote my first Java thread.
"Less understanding" means - they didn't close the socket on error,
their thread might block indefinitely... But the program _will_
work well enough. Yet, the programmer has zero knowledge of
exception-safe code, multi-threading issues. Possibly, he isn't
even on a way to learn it. This scenario might hold for any
language, but IMHO it's especially true with Java thanks to
bad marketing. Things would be much different if mentioned examples
explained on how things should be done properly and why.
Come to thing of it, maybe it's different now than when
I was starting with Java...
I forgot - what does this thread have to do with anything
else than C++?
[snip]
>>
>> Further static_cast is redundant in my suggested method, and the "T a
>> = T(...);" construction form is also redundant. Construction of "T a
>> = ..." is considered an explicit context (just another form of direct
>> initialization).
>
> Unfortunately WG21 felt that it could not make that change. I cannot
> remember all the detail but it wasn't laziness or an arbitrary decision.
> Instead we provide a new universal initialisation syntax that we are
> trying to ensure is usable in alll places where initialisation takes place.
I don't have any more arguments considering implicit conversions,
except the ones already mentioned. Just one correction.
Even if T a = T(...) becomes redundant, it still leaves the assignment.
res = Ptr_Base( compose( Ptr_Base(x), Ptr_Base(y) ) );
or
return Ptr_Base( compose( Ptr_Base(x), Ptr_Base(y) ) );
IMHO, refactoring is also an important issue.
I personally like longer names. Who was it that said that
good programmers don't write comments, the length of variables
doesn't leave the space on the disk? Well, I try to be good,
but simply can't find a granny that would allow me to get
her to the other side of the street.
--
Dragan
? The only meaningful statement I can derive from this paragraph is
absolutely false.
-Andrew.
--
My statement was regarding any specific implementation of a C++
compiler and not the standard itself.
> You've postulated that this can't always be optimal,
> but made only an unsupported assertion that this can't always be
> optimal. You've also failed to show that whatever other
> implementation you think might (at least ocassionally) be superior
> can't possibly be used in a conforming implementation of C++.
You have misrepresented my statement. The reason that any specific
implementation of class memory layout, or virtual method dispatch, or
any other general-purpose structure for solving a large enough set of
problems cannot be optimal for each of those problems, is that there
are opportunity to use problem-specific knowledge to optimize the
system with more primitive components. This should be really quite
obvious.
Look at it this way, you can implement every C++ program in asm using
only asm-specific features. The converse is obviously and provably
untrue.
-Andrew.
--
Well, you think wrong. Here is evidence in support of my claim:
http://www.cwjobs.co.uk/Content/ContractDeveloperDemand.html
You can see that the rate for Java programmers is 52.8 and that for C+
+ is 55.4. The gap has closed since last I looked, a few years ago it
was over 10%, now it is close to 5% as Java developers and projects
have gotten older.
> > As people tend to stick to the language they know, this is a function
> > of how long the two languages have been around. ie C++ guys are
> > simply older than Java guys on average.
>
> Actually the best programmers use multiple languages
Did I dispute that? Where exactly did I claim that the best
programmers do not use multiple languages?
> > It is also easier to get something working in Java for a less skillful
> > programmer. A certain logical error may cause a memory overwrite in C
> > ++ - whereas the same error in Java only causes a memory leak.
>
> That does not look right to me.
If an object is deleted in C++, and then used, it will cause a memory
corruption. As objects in Java are only deleted when they become
unreachable, that object that was supposed to be deleted is still
around, hence a memory leak instead of a corruption.
> > A cunning recruiter may spot the first reason and hire into C++. Not
> > because it is a better language, simply because people that use the
> > language are better. :) It's a strange world.
>
> Again, pure speculation.
I have firsthand knowledge of a company adopting the policy to use C++
instead of Java because they believed that C++ programmers were in-
general more competent developers than Java programmers.
I can also say from working with several hundred C++ and Java
programmers that on average the people that are working primarily in C+
+ are older and more experienced - and hence more competent.
-Andrew.
The last two are completely incorrect. When an object is past to a
function taking a reference to one of its base classes, no copy
constructor is called. The pointer to the object is placed on the
call activation frame of the function. This example is not relevant
to the discussion of when to use implicit construction instead of
explicit construction.
-Andrew.
--
As I've already stated we don't use the STL for exactly these kind of
reasons. While it is very general purpose and reliable, the coding
conventions it uses are terrible. Iterators are also dangerous and
have strange semantics. We use an enumeration loop and ranges
instead. Much saner.
> Also, reducing it to something as short as P_ and PC_ only makes them
> hard to understand to me. I don't want some kind of name mangling
> inside the code itself...
As I said usually there are a small number of type prefixes and they
are used all over the place, so it is sensible to give them short
names. You only need to learn once that for your reference-counted
scheme that P_T and PC_T refer to mutable reference-counted pointer to
type T and const reference-counter pointer to type T once, and you
have learned it for the entire codebase. Consider that in the CLR
version of C++ that "T^" represents a GC pointer to T. They chose to
call it "T^" and not "Smart_Ptr_T" for a reason.
> In other situations, you want to use implicit conversions to mimic the
> Derived* -> Base* implicit conversion done by the language (you still
> haven't said whether you wanted to ban that one either). The code
> given above actually used both kinds as an example of how implicit
> constructors allowed to implement types that had the same proprieties
> as pointers.
When there is no secret conversion code executed and you are referring
to an object by reference or pointer to one of its base classes, it is
most likely safe enough to drop a cast, as nothing is really
happening. No memory has changed...
ie
class D : B {...}
void f(B*);
D* p = ...;
f(p); // and not f((B*)p);
...is okay, because the programmer reading the code should really know
what the base classes of the types he is looking at are - and when f
is called no conversion is taking place, nothing magic is happening.
The pointer to D* is placed on the stack and f is called. All the B
methods just operate on the D instance like it was a B instance
(except for virtual methods of course, which further shows that even
*less* is really happening in the conversion from D* to B*).
This is a distinct situation from calling silently implicitly calling
some constructor to some overloaded function, (even just a smart
pointer's constructor), and executing some code that could be from
anywhere.
-Andrew.
Well my position isn't that it is theoretically true, my position is
that it is true. I never gave any limit to the practicality of it,
because it wasn't pertinent to the point - which was that everything
is *possible* from all of C, C++, Java, C# and asm - it is simply a
matter of what things they make *easy* versus what things they make
*difficult*.
> > The point is the C++ compiler doesn't have any magic power to generate
> > faster code than C. The "meta-data" system you speak of (virtual
> > methods, inheritance, constructor chaining, RTTI, etc) can all be
> > implemented in C.
>
> There's no magic involved, but templates (in particular) give a C++
> programmer a _reasonable_ ability to do things that really are not
> reasonable in C at all.
Ahhh, no. Instead of using templates, you can just type out every
template instantiation by hand, substituting the type parameters in
the same way as the preprocessor resolves macro calls. This would
result in identical object code, and is exactly what the compiler
does.
What templates do is make things *easier* and more convenient. They
provide no magically new ability that did not exist previously without
them.
As I've said (several times now) this fact means that we need to make
comparisons between these languages based on how *frequent* a given
feature is used, not just that the need for a certain feature
*exists*.
-Andrew.
--
:) Identifier length, as any question of form, is a question of
balance. I will say however that many people advocate long identifier
names to hide from naming conflict, when they should just use
namespaces instead.
-Andrew.
--
[ ... ]
> Ahhh, no. Instead of using templates, you can just type out every
> template instantiation by hand, substituting the type parameters in
> the same way as the preprocessor resolves macro calls. This would
> result in identical object code, and is exactly what the compiler
> does.
More accurately, you could _theoretically_ do that, and if you did,
it _might_ result in simlar object code and is somewhat similar to
but definitely not the same as what the compiler does.
The difference between us seems to be pretty simple: you're stating
things that should theoretically true as absolute facts. I've done
actual testing, and found that even though what you're claiming
theoretically could be true, in reality it's not -- for at least one
compiler (the testing I did was with MS VC++ 7.0, FWIW) instantiating
some templates by hand produces _different_ (and measureably less
efficient) object code than the compiler produced on its own from the
template.
> What templates do is make things *easier* and more convenient. They
> provide no magically new ability that did not exist previously without
> them.
Magical, no. In theory (see how that keeps coming up, no matter how
often you try to deny it?) there's nothing to stop a compiler from
producing identical object code, but as I pointed out above, in
reality it's just not the case.
Even if it was the case, it's a purely theoretical possibility:
nobody's ever done it, and there's no real chance that anybody will
ever even attempt to do so either. The reason for that is pretty
simple: especially when you use policy templates, a few dozen lines
of C++ code could take tens or even hundreds of thousands of lines of
non-template code to completely duplicate. If you really insisted on
doing it, the only reasonable possibility would be to write your own
equivalent of C++ templates (possibly with different syntax) and use
some other processor to generate all the possible code combinations.
> As I've said (several times now) this fact means that we need to make
> comparisons between these languages based on how *frequent* a given
> feature is used, not just that the need for a certain feature
> *exists*.
Once again, you're drawing a conclusion without benefit of supporting
logic.
As is clear to anybody with an IQ higher than their hat size, we
don't "need to make comparisons between these languages" in any way,
at all.
--
Later,
Jerry.
And what convinces you that "any specific implementation" is more
constrained than the standard requires? If a compiler author decides
to implement 7 different forms of virtual method dispatch, is there
anything in the C++ standard to prevent that from happening? If that
was 38 or a 1000 instead of 7 would the answer be different?
[ ... ]
> You have misrepresented my statement. The reason that any specific
> implementation of class memory layout, or virtual method dispatch, or
> any other general-purpose structure for solving a large enough set of
> problems cannot be optimal for each of those problems, is that there
> are opportunity to use problem-specific knowledge to optimize the
> system with more primitive components.
Yet again, you're simply postulating a theoretical possibility.
As the old saying goes, "show us the money". If you want to claim
that this really IS true, you need to start by showing a class memory
layout, or virtual method dispatch (or whatever) that 1) improves
efficiency in solving at least real problem, and 2) is impossible to
implement in C++.
> This should be really quite obvious.
As often seems to be the case (especially on Usenet) "obvious"
appears to mean something like: "I think this should be true, but
can't provide a single shred of real evidence to support the claim."
--
Later,
Jerry.
I claim that the set of programs you can write in all of the languages
we have discussed is the same, and I have given logically sound
arguments for why this statement is correct.
You seem to accept my statement as "theoretically true", but not as
"really true" by some other definition.
If you refuse to accept a logical argument, I am afraid I do not know
what else I can do - except perhaps writing every possible program in
each of the languages - and last I checked, I don't have an infinite
amount of time to spend on convincing you. :)
-Andrew.
--
And what exactly were these optimizations that you claim the compiler
made with an automatically generated template instance that were not
made with a manually written template instantiation?
It's easy to back up any claim with "I tested it once" and not post
any supporting evidence. How do we know that you didn't make a
mistake in your handwritten template instantiation?
> As is clear to anybody with an IQ higher than their hat size, we
> don't "need to make comparisons between these languages" in any way,
> at all.
That is an absolutely ludicrous statement. When you are starting a
new software project how do you choose which language to use if you
haven't compared the alternatives?
-Andrew.
--
[ ... ]
> And what exactly were these optimizations that you claim the compiler
> made with an automatically generated template instance that were not
> made with a manually written template instantiation?
Having the severe disadvantage of knowing what the word means, I'd
say "optimization" didn't enter into it. The code generated was
different, and happened to be faster for one than the other, but I
saw no indication that the optimizer entered into causing the
difference (specifically, the same difference happened when
optimization was disabled).
The difference was simply in generating two code sequences that
produced identical results, but happened to run at different speeds.
If you really want to get into to off-topic specifics, one produced
code like:
mov ebx, num1
mov eax, num2
shl eax, 2
add ebx, eax
while the other produced code like:
lea ebx, num1 + num2*4
My guess is that the difference had little (if anything) to do with
template instantiation per se -- it was _probably_ due to the C
compiler's code generator being slightly out of sync with the C++
compiler's. In the Intel x86 architecture, LEA stands for load
effective address -- in this case, it was being used for something
that wasn't related to an address at all. Depending on the age of the
CPU, that can be an optimization -- specifically, from the 186
through the 486 (or so), there's a special address unit that handles
effective address calculations separately from other calculation, and
can handle _any_ (legal) effective address calculation in a fixed
period of time.
With the Pentium Pro, however, the microarchitecture changed quite a
bit -- the CPU started to support parallel execution of instructions
as long as there weren't dependencies between them (e.g. if the
second depended on the result from the first). Depending on the exact
CPU, there are also some limitations on decoding -- there's typically
only one decoder for long complex instructions, but two or more for
short, simple ones.
That means that on a recent CPU, the LEA can be a pessimization --
even though we've reduced four instructions down to one, there are
extra limitations on decoding that instruction, and possibly on
executing it as well. What get scheduled in parallel are the micro-
ops that result from decoding, and while micro-ops were once
documented to some degree, I don't believe such documentation has
been available for some time now.
> It's easy to back up any claim with "I tested it once" and not post
> any supporting evidence. How do we know that you didn't make a
> mistake in your handwritten template instantiation?
Both code sequences executed correctly. For that matter, most people
have no clue of how to do timing that's accurate enough to show the
difference outlined above -- IIRC, we're talking about a difference
of one or two clock cycles.
> > As is clear to anybody with an IQ higher than their hat size, we
> > don't "need to make comparisons between these languages" in any way,
> > at all.
>
> That is an absolutely ludicrous statement. When you are starting a
> new software project how do you choose which language to use if you
> haven't compared the alternatives?
The capabilities of the languages are rarely more than a minor factor
in such decision. Major considerations are things like availability
of implementations for all target platforms of interest, amount of
training I'll need to supply for people to use the language
competently, and likelihood that the language will continue to be
supported for the projected life of the project.
There are a few situations where things like raw speed or memory
usage matter, but they're pretty rare, and even when they become
relevant, a quick test with a relevant benchmark produces
_drastically_ more information than something like your speculation
about purely theoretical possiblities.
--
Later,
Jerry.