Maybe the results of a function call may not be completely understood by the programmer, but from the syntactical point of view it is easy to figure out what is going on in the program.
And, to put it clearly, the problem is not that the function or class is doing something we don’t know. The problem is mostly in trying to figure out what is happening in a given line of code. In C is very easy to figure out what is being called (up to macros): just read the manual for the functions being used and you will have a good idea of what is being executed.
After sorting through this pile of caveats, what point remains?
In C++, on the other hand, a large number of things can be happening in a single line: an overloaded operator, a type conversion, a template instantiation, an overridden function in one of a dozen of classes, a constructor called in some part of the hierarchy, in some namespace…
It isn't that hard to figure things out in C++. An operator is an operator is an operator; there are a fixed list of symbols that can be used as operators. An operator may be defined by the language, or it may be user-defined. Template instantiations are visible. (I don't recall whether you need <> when a template has default parameters, but in that case it just acts like a regular class anyway.) Constructors are just functions that get called when objects are created. Granted, you can create a confusing situation by using multiple inheritance, but multiple inheritance is one of C++'s nuclear options, like macros in C. (I suspect the author would blame C++ for having multiple nuclear options instead of just one.)
Namespaces are very nice for organizing code, and almost every language has some equivalent mechanism. I think the burden is on the author to argue that everyone else is wrong about this.
I'll give him type conversions. Those can be tricky and unexpected, and it's backwards that you have to declare your constructors "explicit" to prevent them from being used in type conversions. It should be the other way around; you should have to declare constructors "implicit" in the rare cases where you want to enable conversion. However, once you're paying attention (and I agree you shouldn't have to), it isn't difficult to figure out when type conversion is happening, simply because you'll see that an argument of a certain type is being passed to a function that cannot take that type. If it compiles, then a type conversion is being invoked. Where people get really confused is when they're trying to take advantage of type conversion, but the conversion they think is happening isn't actually happening. Personally, I've rarely messed with it, so I don't care.
You have to be smart enough to figure out what is going on
I can't tell you how much it hurts my head to read C or how many times I've thought I just wouldn't be smart enough to be a C programmer. It's a different kind of thinking. I'd much rather read a few hundred lines of C++ full of tricky constructs than a few thousand lines of C full of buffer allocations and deallocations and return value checks. My brain overflows and I can't keep track of the high-level shape of the code. Trying to figure out how C code works when I don't already know its purpose is very hard for me. To each his own, I guess.
> It is not a surprise that large scale projects such as the Linux kernel use C without any problem.
I'm sorry, but a post that lacks intellectual honesty when discussing a rather complex topic such as this one just shouldn't make it to the HN front page. If you cannot bring yourself to at least admit in passing that increased expressiveness of a language can be beneficial in some situations, then I'm not even sure what to tell you. Your post is not weakened by admitting this, but I feel like the perception is that you must take a hardline approach.
As I've said before, these sorts of blog posts skate by simply because they tell HN what it wants to hear without actually contributing anything new to the discussion. Thus, it is bikeshedding, and, ergo, not 'highly interesting' (per pg's guideline). Additionally, this topic comes up repeatedly, and I'm not certain why it needs constant discussion.
I understand your point on increasing expressiveness, but how far do you need to go into post-incrementing C before you decide you'd better use a language made with the specific purpose of supporting such expressiveness rather than bolting it on top of C.
A "fun" exercise in C++ is to take a line of code that makes extensive use of advanced features, like templates, smart pointers, etc. and step through a -O0 compile with gdb. I was shocked, in some cases, about how many different functions were called for what looks like a simple expression.
This can lead to some interesting errors. For example, writing
void foo(const std::string s)
by mistake, instead of
void foo(const std::string &s)
won't result in a compiler error, but will result in an unusually large, and unexpected overhead in calling foo.
On the other hand, STL is great (less the error messages). I sorely miss it when programming in C.
Well, C++ got at least one thing right: deciding to support parametric polymorphism (or "genericity"). Templates are quite ugly, but at least they do the job.
It seems like the thesis is that C++ is a more complicated language, and therefore the code produced in it is more complicated and harder to comprehend. This line of reasoning is specious.
This article sounds like a lesser version of a Linus rant I read once and even he used the strawman argument of "in C you can always see what's going on". Does nobody in C use function pointers?
I say horses for courses and if you use operator overloading and templates to make your C++ code overly cute then it's not the fault of the language.
C++ can be quite readable, but C can't be particularly magical. If I were Linus and I spent my days reviewing patches from all sorts of folks to a very large codebase, I'd be inclined to prefer a language that can't do anything unless you ask for it.
Readability depends on the writer, and if someone is trying to duplicate the functionality of C++ in C I expect they're liable to do a worse job than using C++ directly. void* pointers are one example of why.
The usual complaint about C++ is the misleading compiler errors when one uses the STL. I disagree there are other readability issues. Encapsulation and polymorphism exist precisely because you don't want to know what's underneath. It goes without saying that overusing operator overloading is bad design, not just a readability issue.
[+] [-] dkarl|15 years ago|reply
And, to put it clearly, the problem is not that the function or class is doing something we don’t know. The problem is mostly in trying to figure out what is happening in a given line of code. In C is very easy to figure out what is being called (up to macros): just read the manual for the functions being used and you will have a good idea of what is being executed.
After sorting through this pile of caveats, what point remains?
In C++, on the other hand, a large number of things can be happening in a single line: an overloaded operator, a type conversion, a template instantiation, an overridden function in one of a dozen of classes, a constructor called in some part of the hierarchy, in some namespace…
It isn't that hard to figure things out in C++. An operator is an operator is an operator; there are a fixed list of symbols that can be used as operators. An operator may be defined by the language, or it may be user-defined. Template instantiations are visible. (I don't recall whether you need <> when a template has default parameters, but in that case it just acts like a regular class anyway.) Constructors are just functions that get called when objects are created. Granted, you can create a confusing situation by using multiple inheritance, but multiple inheritance is one of C++'s nuclear options, like macros in C. (I suspect the author would blame C++ for having multiple nuclear options instead of just one.)
Namespaces are very nice for organizing code, and almost every language has some equivalent mechanism. I think the burden is on the author to argue that everyone else is wrong about this.
I'll give him type conversions. Those can be tricky and unexpected, and it's backwards that you have to declare your constructors "explicit" to prevent them from being used in type conversions. It should be the other way around; you should have to declare constructors "implicit" in the rare cases where you want to enable conversion. However, once you're paying attention (and I agree you shouldn't have to), it isn't difficult to figure out when type conversion is happening, simply because you'll see that an argument of a certain type is being passed to a function that cannot take that type. If it compiles, then a type conversion is being invoked. Where people get really confused is when they're trying to take advantage of type conversion, but the conversion they think is happening isn't actually happening. Personally, I've rarely messed with it, so I don't care.
You have to be smart enough to figure out what is going on
I can't tell you how much it hurts my head to read C or how many times I've thought I just wouldn't be smart enough to be a C programmer. It's a different kind of thinking. I'd much rather read a few hundred lines of C++ full of tricky constructs than a few thousand lines of C full of buffer allocations and deallocations and return value checks. My brain overflows and I can't keep track of the high-level shape of the code. Trying to figure out how C code works when I don't already know its purpose is very hard for me. To each his own, I guess.
[+] [-] mattgreenrocks|15 years ago|reply
I'm sorry, but a post that lacks intellectual honesty when discussing a rather complex topic such as this one just shouldn't make it to the HN front page. If you cannot bring yourself to at least admit in passing that increased expressiveness of a language can be beneficial in some situations, then I'm not even sure what to tell you. Your post is not weakened by admitting this, but I feel like the perception is that you must take a hardline approach.
As I've said before, these sorts of blog posts skate by simply because they tell HN what it wants to hear without actually contributing anything new to the discussion. Thus, it is bikeshedding, and, ergo, not 'highly interesting' (per pg's guideline). Additionally, this topic comes up repeatedly, and I'm not certain why it needs constant discussion.
[+] [-] rbanffy|15 years ago|reply
[+] [-] mv1|15 years ago|reply
This can lead to some interesting errors. For example, writing
void foo(const std::string s)
by mistake, instead of
void foo(const std::string &s)
won't result in a compiler error, but will result in an unusually large, and unexpected overhead in calling foo.
On the other hand, STL is great (less the error messages). I sorely miss it when programming in C.
[+] [-] loup-vaillant|15 years ago|reply
Well, C++ got at least one thing right: deciding to support parametric polymorphism (or "genericity"). Templates are quite ugly, but at least they do the job.
[+] [-] antubbs|15 years ago|reply
[+] [-] brimpa|15 years ago|reply
',' is an operator too, not just punctuation.
http://en.wikipedia.org/wiki/Comma_operator
[+] [-] zwieback|15 years ago|reply
I say horses for courses and if you use operator overloading and templates to make your C++ code overly cute then it's not the fault of the language.
[+] [-] supersillyus|15 years ago|reply
[+] [-] sn|15 years ago|reply
[+] [-] sn|15 years ago|reply
[+] [-] ignifero|15 years ago|reply