"Finally, which version is likely to be the most efficient? Yes, the C++ version, because it does not have to count the argument characters and does not use the free store (dynamic memory) for short argument strings."
I just compiled the C code and the C++ code using:
gcc -std=c99 -Wall -Wextra test.c -o test
g++ -std=c++11 -Wall -Wextra test.cpp -o test
This is the archetypal problem with benchmarks: not only are they hard to design, but people will incorrectly nit-pick the results.
You count the number of allocations, without looking at the big picture: this is a toy example. It provides the hard-coded input to the function. This results in the C version not having to allocate memory for the hard-coded literals and the function problably being inlined and optimized away and the strlen() calls being equally optimized away as the length of the strings are known at compile time.
In the real world, these would come from some input where they would need to be allocated on the heap, just like the C++ version does. It would use just as many allocations, except they will all be hand-rolled, hand-held, error-prone, with every bit of memory management explicit. C++ is superior in every measure here. Only in toy programs and hard-coded strings does C wins.
If the goal was to have as few allocations from hard-coded strings, then just write "foo@bar" and avoid the concatenation altogether. That is not the point of the example.
(Also, the newer C++ standard support std::string literals, FWIW.)
Why is "str" a "const char " rather than a std::string? Tested using "-std=c++14" with both gcc version 4.9.1, clang 3.5.0 and with both libstdc++ and libc++ ?
[edit] If you're the guy who does the MSDN videos on C++, thank you*. They've been incredibly useful to me.
Sorry, wasn't checking my HN comments frequently (unlike Reddit there's no little red icon).
"meow" is a traditional string literal, whose type is const char [5] (array of 5 const chars). When you say "auto", you get the same deduction as when you pass something to a template foo(T t) taking by value. This triggers "decay", where arrays decay to pointers. Hence auto (and T) is const char *.
"meow"s is a user-defined literal (the Standard Library is a user as far as the compiler is concerned), for which you must include <string> and say "using namespace std::string_literals;" or something equivalent. The specification for UDLs says that this calls operator""s() which returns a std::string by value.
It may not be the point of the example but it's mentioned explicitly by the article's author as a feature. Doesn't that make allocations in the example fair to comment on?
The articles promise is actually terribly dubious. Firstly, in the real world, name+'@'+domain is likely to exceed the capacity for any SSO anyway. Even the example composition, which is 21 chars, may resort to heap allocation under some implementations. It's a bad solution to depend on it for performance.
Secondly, as you discovered, the current GNU stdlibc++ implementation of std::string will always allocate for short strings. Even 1 byte. This is for binary back-compat and will hopefully be fixed in GCC 5.0. For now try it with Clang and libc++. See[0] for more information and a comparison of current implementations.... the standard doesn't require SSO at all.
Thirdly, and getting to the deeper issue, once the heap is involved the C version is nearly (it needs length params) optimal and will still always be more efficient than the C++ code. Why? The C++ solution uses uses operator+ which is fundamentally performing a different and less general algorithm. For composition it will introduce some amount of repeated work which even the best link-time optimizing compilers probably won't remove, and may, depending on the length of the trailing string segments, and the growth strategy used under the covers, force two allocations. The standard doesn't give complexity guarantees for mutation on std::string at all, so the resulting performance could, on some platforms, be very bad. In all cases you will be able to construct an input that forces more than one allocation (exponentially growing segment sizes).
There's also another efficiency issue in the naive op+ approach... C++s allocator specification doesn't provide realloc() so a portable solution will always use more peak memory, on average, when it is forced to reallocate.
The C++ solution is just using the wrong algorithm for the job. You can write, and I have toyed with[1], a variadic strcat() function that will be optimal in the generic case. The standard library just lacks one. It's embarrassing, but fairly understandable given that variadic templates didn't come in to the standard until 2011. I'm hoping if they introduce one it'll be called scat() :-)
I'm a big C++ fan, but we shouldn't be deluding ourselves here. Fortunately, these things can be fixed within the framework of the current language.
That's the good thing about C. With C++, you don't know what is being allocated, where. With C, it is explicit. You know what's happening if you call malloc.
And this compose function could easily be written in C to take the "addr" as an argument to be filled, rather than provide it as a return value. Then it can be entirely stack allocated. The caller would need to make sure that the "addr" array was large enough of course.
I started learning C and C++ together at the same time a few years ago. I prefer to code in C++, using the standard template library, but there's always this extremely annoying little voice in the back of my head saying, "you could do this more efficiently if you operated on character arrays instead of a std::string"
> That's the good thing about C. With C++, you don't know what is being allocated, where. With C, it is explicit.
You're complaining about allocation in the C++ standard library. How many allocations does sqlite3_open_v2 make? How about git_repository_open?
C code hides allocation just as much as C++ code does. Only C++ provides simple mechanisms (destructors) to make sure everything gets cleaned up when the stack unwinds.
I'll agree that std::string does the wrong thing here. The C++ standard library gets strings wrong in a lot of ways. That's why vector usually provides better examples.
On the other hand, I think one of the good things about C++ is that you have a choice. If you wish to write code that explicitly allocates memory for a string of characters you can. You can even wrap it up in a class to make it easy to use.
Right. Also, the C version would be cleaner using the printf family of functions. Idiomatic C would use snprintf and accept a buffer. If you insist on returning a heap-allocated object, you could use asprintf, which is not universal but is common. Either way it's a one-liner.
To paraphrase Don Knuth, "I have only proven it correct, I haven't tested it."
To be fair, he says "likely to be" and results may vary between compilers. He also makes reference to the speed penalty of calling strlen(), which isn't necessary with std:string because it stores the length. Even with more allocations, it's possible the C++ version runs faster. It should have a lower computational complexity, meaning it would be faster if the input strings were much longer than they are in this example.
In my experience, C++ compilers are great at eliding temporaries and various constructor calls (and related memory allocations), but only if optimizations are turned on.
I'm looking forward for the next part, as the last three myths are the ones I'm waiting for their answers, as they should be pretty interesting.
C++ has come a long way, and I really feel C++ 11 could very well replace Java as a learning language for computer science students (I don't think it'd be a good choice for an introduction to programming for non CS students), and I see many benefits in this, the biggests being IMHO C++ NOT being fully object-oriented, and the possibility to do either low- or high-level stuff, depending on what you're teaching.
My biggest issue with most garbage collection implementations is the lack of RAII. While I love the ease of reading and writing Python or Lua, I'd love to be able to create a local object, like an open file or a database connection, and have it destructed immediately on return.
Either with a using/try with resources/with construct, or by using closures.
// Pseudo language with FP capabilities
let withFile filename action =
let fd = open filename
action fd
close fd
withFile "config.sys" (fun fd -> write (readlines fd))
Real production code would also take care about proper closing the file in case of errors.
What sort of C programmer wouldn't use snprintf to do the formatting? If the C++ program can use std::string then it's only fair to let the C program import <stdio.h>. And honestly printf is so much nicer to use than iostreams I've always considered the relative ease of putting together a formated string one of C's strengths relative to C++.
Rule #1 of systems programming: "Never check for an error that you don't know how to handle." This is, of course, a joke, but it has a kernel of truth.
I've worked on production code where, if allocation fails, the system fails, too, and restarts. The allocator never returns. There's usually some kind of helper system watching for the failure, which can take a dump or at least a stack trace.
The theory is that any attempt at recovery is going to be a disaster; not well tested, and probably doomed in the face of memory pressure anyway. On 64 bit systems the memory ceiling is essentially infinite, but reaching the point where we start to swap is horrible and a pyrrhic victory at best, so instead let's do a controlled crash and log enough information so we can analyze and fix the actual problem (which is a misunderstanding of load and resources, or maybe a bug or an attack where we're trying to allocate twenty gazillion bytes).
That's one philosophy. Another (which was prevalent on the Mac in the 80s, in user applications) is to have a reserve; you test the crap out of the code and give it enough memory in some kind of emergency mode to succeed at getting back to a normal state, or at least quitting in an orderly fashion (ditch dynamically loaded code, fonts, whatever) so that the user's work isn't totally lost.
There's a lot to be said for both designs, and of course these don't cover the case when you're writing library code that can be used by anyone, where reliably returning failure to the caller is important. Here you don't have a choice other than to check and be robust.
I worked on some safety critical systems in C. The philosophy was treat the code like a little person running around. The person (the code) always wanted to know where the fire escape was. The fire escape always needed to be very easy to use and very reliable. Every function had little fire escape handler to clean up on error. Higher level functions had to decide how to put out the fire (speaking metaphorically here but we really could have started fires) or to raise the alarm to a higher level.
At the very top level, the only thing we could do was save a log then reset (least worst option). Too many resets too fast and we'd take ourselves offline and scream for a human (again, least worst option).
Malloc will never fail on Linux due to running out of RAM. However, it will fail for other reasons, such as running out of address space. You can easily run out of address space in 32-bit processes, since you only have 4GB of address space, or less. It's much harder in 64-bit processes, but since you can allocate vast chunks of memory without having them be backed by RAM, you can accomplish it there as well.
While address space is huge, RAM isn't infinite. Our embedded Linux runs out of memory quite often. Or if the swap space fills up on a server with a disk.
I've spent many hours chasing corrupted memory due to someone not checking malloc() fail.
I chased code that boiled down to assert( ptr=malloc() );
Guess what happens when NDEBUG is defined?
It's not so much that malloc can fail, but that you're likely to get whacked by OOM before you run out of address space on linux. To get malloc to return a NULL, you would have to be allocating more than 4Gb of memory on a 32 bit system, the system has to have no swap, and you have to not have used any of the memory between the last physically available memory, and the memory allocated by the chunk that takes you over the 4Gb limit. Anything else gets you SIGKILLed.
Swap has nothing to do with it. Getting malloc to fail is easy:
malloc(-1);
With address space fragmentation, the requested size can be a lot smaller and still fail. With overcommit, you can allocate a ton of memory without using up any RAM.
Memory leaks in Firefox/Chrome might belie that notion.
I've had Python throw MemoryError at me while I was doing some video processing with NumPy. 8GB RAM, 64GB swap. I wasn't careful with my refs and I confused Python's garbage collection (memory leak).
If malloc fails for trying to allocate a few miserable bytes I would claim the game is over. Why would he need a fallback strategy, anyway? The program is tiny and really, really doesn't do anything important.
Matters of taste I suppose, but I've always felt that for tutorial and proof of concept code it's better to skip unnecessry details.
Not every line of code lives in a production environment...
I like C++, but using it to teach beginners who have no knowledge of C or compilers in general is a horrible idea. My college CS101 course was taught using C++, and I was lucky because I already knew multiple programming languages before that.
1. There are a lot of abstractions in C++ that make it very powerful, but the reason those exist would be completely lost on a newbie. Pass-by-reference vs. pass-by-value? Oh yeah, there are pointers too that you might run into, that's almost exactly like a reference but with a different syntax. We have smart pointers instead to obscure that from you, but if you search for examples online you might run into any of these. But don't worry about all of this; these language features only exist because of type safety and performance which you have no clue about. Just change the syntax until it compiles and runs.
2. Programming is more than write->compile. Debugging is as important, if not more, and heaven help you if you're a beginner trying to debug C++. With C you'll at least get exposure to machine code that pretty much looks like the program you wrote.
3. C++11 is an improvement, but it's not ubiquitous and it adds abstraction that someone without a solid C/Assembly background will have very little chance of understanding.
Teaching C++ to someone with a firm grasp of C would be a lot easier and additive. "Here are the new features that can help the compiler make your program more performant or type-safe or fix your memory leaks." Otherwise you're throwing a bunch of solutions at students to problems they've never had.
It is the end of 2014 and I am using C++11 on Windows, OSX and Linux without too many incompatibilities. Of course, you need the latest versions of the compilers but it's ubiquitous enough.
I'm getting so tired of the word myth and how it's used so incorrectly. These may be misconceptions, but they are not myths. In truth, some of these are factually accurate and you're framing them as a misconception to drive a narrative. I can't help but consider that link-bait, even though structured differently, this would be a good article.
First, which of the "myths" are "factually accurate" and in what sense?
Second, I don't find his use of "myth" objectionable, in much the same way I (as a scientist) don't find objectionable the use of "theory" to mean "guess" in non-scientific speech (attempting to impose "guess" as a meaning when discussing scientific theories is anathema). This use of "myth" here is fairly common, at least colloquially in America, as a rhetorical flourish in place of "misconception."
Does anyone choose C++ anymore unless it is required by environment/legacy/performance constraints?
Back in the day I read Bjarne's book cover to cover, used C++ for very large scale projects, and was one of those guys who could make sense of arbitrarily complex pointer expressions.
Now I mostly regret it. Led down the rabbit hold of multiple inheritance, STL being much more complicated than templating in other languages, and watching devs take years to master all of the subtleties.
He alludes (next article) to garbage collection not being more reliable than manually memory management? Yes GC has it's own disadvantages and there are many ways in C++ to mitigate memory management problems, but seriously I wish I had a dollar for every hour of time spent on a memory allocation bug in C++.
I still use C++ because it not only allows me to write high performance code, but also allows me to easily express high level ideas without worrying about too much overhead. I can write generic algorithms in terms of monoids and semigroups and I can write device drivers. I do a lot of signal processing at work, so sometimes these high-level and low-level concepts exist side-by-side in the same codebase.
Edit: I should mention that by no means to I think C++ is perfect. Very few other languages, however, give you the amount of low-level control, and the tools to build cheap, powerful abstractions, like C++ does.
> Does anyone choose C++ anymore unless it is required by environment/legacy/performance constraints?
At work no. Typical enterprise world using JVM and .NET languages.
For side projects, surely yes!
It is the only language supported across all mobile OS SDKs with full access to the hardware.
If you have control over the codebase, the ability to use C++11/14 is quite comfortable, even in terms of memory management.
Now for the typical enterprise projects, with offshore developers at work I wouldn't use it. I already cry when I look at code written in less powerful languages.
Yes, but it seems the whole team (or all teams in large enterprise projects) must know a lot. Otherwise you still and up with a lot of hard to read, unsafe code.
If you start with a very large codebase of old C/C++ you might have to rewrite large parts of it to reap the benefits and in that case you might want to look into other languages as alternatives.
I agree that for a green field project with very good developers C++ 14 looks nice.
Couldn't you say this for any language? As soon as you start applying it to larger projects things start to get more complicated. It's not the same problem per language, but each has their own issues.
Thanks. Very short post though. What I have to say is, that C++11 and onwards had indeed made C++ much more approachable. I think Herb Sutter's talk on modern C++ is also worth a look. [0]
Good luck trying to understand C++ without C. The numerous C++ traps and pitfalls will just look mad to you.
BTW, 'multi-paradigm' is an oxymoron. Not even Scala uses that concept any more.
look at it this way: suppose some alternate universe where there is no such thing as C, it just doesn't exist, nobody ever heard of it. Why would you not be able to completely master C++ that universe? The traps and pitfalls which you consider C, would then just be known as C++ (just as in reality they are C++ now, maybe not just always named as such). I think that's one thing what's being advocated here.
Anyway, what traps do you have in mind specifically?
Much of C++ can only be understood in the context of C. For example, why are string literals not std::string, and what the heck are null-terminated strings about? In your alternate universe, C++ would be a nonsense language full of absurd design decisions. Understanding C is perhaps necessary to understand why C++ works the way it does.
Even further, why are strings considered a linear sequence of 8-bit characters (defaulting to ASCII)? That's madness in 2014! If I give you a sequence of bytes and tell you it's a "string", that's no more meaningful than if I gave you a bunch of bytes and said "this is music" or "this is a picture". Without telling you the encoding you have to blindly guess how to interpret it.
Go does a good job of rectifying this: strings are generally opaque binary objects--indexing into them gives you abstract "runes" (representing Unicode code points) which can then be encoded into various flavors of UTF characters.
"Go does a good job of rectifying this: strings are generally opaque binary objects--indexing into them gives you abstract "runes" (representing Unicode code points) which can then be encoded into various flavors of UTF characters."
There are ways of iterating along a string by "rune" but it's not the default. That said, given that strings in Go are assumed to be UTF-8, indexing into them by rune is expensive and if you actually want to iterate by Unicode char I'm not bothered by having to be a bit more explicit about it.
That promising that "Go strings allow you to index into them to retrieve runes" is overselling Go's string support, of course. That's why I led with that statement, since it is what I was trying to say.
It's best to be correct about how languages work, because when you interest someone in a language via a false statement, they do not end up thinking highly of either you or the language when they discover the falsity. While I'm not sure I'd call myself a Go "advocate", I do prefer that people like and/or dislike it for valid reasons rather than invalid ones.
But if we accept the hypothetical, I think that people in that universe would still identify and separately consider the C-ish subset of C++, much like template metaprogramming is considered its own subset of the language.
So no, I don't think they would understand C++ without first understanding C, even if they didn't call it "C" or consider it to be a completely separate entity.
What's a paradigm? It's a way of viewing things. What ways are there of viewing programming? Well, there's functional, there's structured, there's object-oriented, there's generic, and maybe some more.
Can C++ be used to write non-object-oriented, non-functional, purely structured-programming-style procedural code? Yes, it can. (Essentially, that's the subset that's in common between C and C++.) Can it be used to write object-oriented code? Certainly. Can it be used to write functional code? Again, yes (though not nearly as purely as the Haskell zealots would like - but C++ doesn't enforce the purity the way Haskell does precisely because C++ is multi-paradigm). Can C++ be used to write generic code? Yes. (I have heard that the STL was written in C++ because, at the time, it was the only widely used language that would do what Stepanov wanted to be able to do.)
Four different paradigms. You can write C++ code using any of them, or you can mix and match. That's multi-paradigm.
Or are you arguing that, if you have more than one paradigm, you don't have any? That might be true of a program, though I think that the argument is merely a matter of definition, and therefore not very interesting. But if the language makes it easy to write in several paradigms, it seems perfectly appropriate to call it multi-paradigm.
I think what ExpiredLink1 meant is what millstone says here [1], which I took to mean that although you don't need to know C before learning C++, psychologically it may help to know about C (i.e., that a language called C existed before C++ and that C++ is designed to be mostly backwards compatible with C), so that C++'s design choices don't seem as weird.
Because references can't be NULL in C++, and you can't reseat a reference in C++.
> why does printf exist?
Personally, I think the more interesting question is "why isn't printf extensible?" And, with variable length template functions, it should now be extensible. If it were converted to a template.
> what is everything in the cstdlib?
I can't speak for all beginners, but I can say that when I was a beginner, the concept of language (and library) evolution was never hard for me to grasp. Even in a world where C never existed, I think I could have understood that some the library had old ways to do things, and new ways to do the same thing.
In fact, I can't think of any language that's been around for more than, say, ten years without a little cruft and duplication in the standard library.
I will admit that the textbook I learned C++ from didn't cover C-style string manipulation (other than to say it was error prone). It took a very long time for me to be comfortable doing any kind of string operation without first converting to a std::string. Nowadays I realize that there are often different answers to different questions, and it doesn't bother me.
References in my opinion should actually be called "Address of" operators, then it's much easier for someone new to see why pointers and references exist together.
Printf and the cstdlib - eh yeah I'll hand you that, if someone had no concept at all that a language called C existed prior to C++ this would be confusing without explaining it to them. But a newbie should not be looking at C++ code that uses the cstdlib or printf anyway, they should be looking at streams and true C++ code to begin with.
C++ falls to the no true scotsman fallacy yet again.
If you type in hello world to the internet, you are going to be getting C and C++ versions and you are going to find out that both work. You can't hide the truth from beginners for any length of time. Imagine telling someone that they can't look stuff up on the internet in case they get exposed to "the wrong stuff"?
Bit of a strawman on googling for results - sure I'm not suggesting you can hide the fact it's a relative of C, but I know when I first started out every question I would ask the almighty google oracle would be appended with "C++" - "Why is my code crap C++" - so usually you'd get an actual C++ style solution.
And so far as google and streams, well that's their opinion and fits into their particular style. There's plenty of dev houses that would tell you to avoid C style string manipulation due to the dangers of stack overflows etc.
Anyway I'm not wanting to fall into a C vs C++ argument, I'm merely pointing out that it's very possible for someone to pick up C++ without having the slightest bit of knowledge of C to begin with. I sure didn't know any C to start with.
Pointers aren't even there for supporting a legacy language, they have their use and are distinct from references. Mixing the two and thinking they are equivalent (though they are close in what they do) is a mistake.
Of course this isn't an argument, but this shows that even though C++ doesn't prevent you from falling into the same trap that are present in C, it does give you the tool to at least allow you to do better than C to avoid these traps, notably thanks to RAII.
Oh and let's not forget nullptr, which is a blessing compared to NULL.
Pointers are still a feature of both languages.
Pointers in C++ are basically the same than in C, just with mechanisms that solve the ownership and some safety problems.
So yes, it allows the same holes, but at least you have tools to avoid falling in them.
Starting to sound like a broken record. No, the answer in all cases can also be found in C++, and understood in C++ terms alone. C++ is a SUPERSET of C, not just an arbitrary language + C.
i will now abstain from further conversation - i will leave you with this - i don't care to read your answer in case i have to respond and you might have to deal with some more repetition:
using the language of sets, the set of C++ minus the set of C, with what is now left:
How it allows for all the C pointer features, while adding a little bit more safety.
MODULE examples;
IMPORT Terminal, SYSTEM;
(* procedure with reference parameter, no need for pointers *)
PROCEDURE ChangeParam (VAR changeMe: CARDINAL);
BEGIN
changeMe := 25;
END ChangeParam;
(*
procedure with a slice, bounds given by HIGH(changeMe) and LOW(changeMe).
also a string in this case
no need for explicit pointers
*)
PROCEDURE DumpLetters (VAR letters: ARRAY OF CHAR);
VAR
i : CARDINAL;
BEGIN
FOR i := 0 TO HIGH(letters) DO
WriteChar(letters[i]);
WriteLn;
(* Or just use *)
WriteString (letters);
END DumpLetters;
VAR
anInt : CARDINAL := 12 ;
aPtr : POINTER TO CHAR;
aBuffer : ARRAY [1..2] OF CHAR;
anAddr : ADDRESS; (* think void* *)
aPtr2 : POINTER TO CHAR;
BEGIN
WriteInt(anInt); (* writes 12 *)
ChangeParam (anInt);
WriteInt(anInt); (* writes 25 *)
DumpLetters ('A simple string');
NEW(aPtr); (* The compiler knows the size, no need for sizeof, but you can use ALLOCATE(aPTR, SIZE(CHAR)) if you want *)
(* This is where it shares the same problems with C, due to manual memory control *)
DISPOSE(aPtr);
(* do pointer arithmetic if really required *)
anAddr := SYSTEM.ADDADR (SYSTEM.ADR(aBuffer), SIZE(CHAR) * 1);
aPtr2 := SYSTEM.CAST(POINTER TO CHAR, anAddr); (* aPtr2 now points to aBuffer(2) *)
END examples;
So the compiler will mark this module as unsafe since the meta-package SYSTEM is being used.
All Modula-2 successors, have GC support, as such the DISPOSE() would have to be written as SYSTEM.DISPOSE() and could only be applied to untraced pointers.
ok: why do structs exist and why are their members public? why don't you just use classes with public?
i have been taught extremely poorly and am many years your junior. K&R/stan lipmann's "inside the C++ object model" was a huge "oh ok, I've been basically lied to the entire time"
people keep using this "new C++ devs" proviso, it's a red herring. to remind you:
> What exactly do you need to know about C, that is not part of C++, to understand C++?
why can't you memcpy classes but you can structs? it's a confederation of languages, i think coldtea's idea of representing it as a superset is excellent, you cannot understand C++ by taking out its C subset.
if that's your attitude where it's not the real reason, but you think your explanation is sufficient:
your withholding information is limiting my education and as a student i would hate that. your explanation isn't an explanation, it's an order/instruction without explanation, C++ is full of this. you only get to "why" with C.
memcpy(s1, s2, sizeof(struct_type)); // wat is the problem :?
> There were no C lectures, nor teaching what is C or C++.
> Back when a I was teaching assistance in the late 90's, we never talked about C in our C++ classes
What exactly do you mean by the first statement? You mean you never taught people the difference?
You can't properly explain the existence of the struct data type without C, this is nothing to do with FORTRAN.
> I never saw anyone speaking about learning BCPL to use K&R C, or having to learn Pascal to use Ada or Modula-2.
that is not the argument I am making. the argument people are making is that you can learn C++ without learning C. they seem to be forgetting that in the process of understanding C++, you have to learn C. if you do not understand C, you cannot understand C++.
to transplant your argument, if you take out all the keywords from BCPL that are shared with C and try to learn C without using them, you aren't going to get anywhere.
> memcpy(s1, s2, sizeof(struct_type)); // wat is the problem :?
This only works if struct_type is a POD.
Meaning no member functions, no virtual functions and no inheritance.
> What exactly do you mean by the first statement? You mean you never taught people the difference?
No, why should I, it is all C++.
It might be called struct, but only the keyword is the same, the semantics are quite different.
So why bother students heads with needless details how a C compiler sees a struct and a C++ sees a struct?
A struct is a class with default public access, that is all.
> You can't properly explain the existence of the struct data type without C, this is nothing to do with FORTRAN.
Why? It is a C++ data type. Why should I use another programming language to explain it?
> If you do not understand C, you cannot understand C++.
I wonder how my students could manage their exams.
You have to learn C++ relation to computer hardware and the design decisions behind it.
This means computer architecture and Assembly, C is not required.
Sure one can explain that some decisions are related to the desire to have C++ interoperate with zero attrition in the C Eco-system, but that isn't required to explain C++ language features.
Do you also want people to learn C before C# and D?
Keywords and programming concepts are common across programming languages.
the question is why does a struct exist at all. just because they could pass an exam is not a great indicator of understanding. if your students put member functions, virtual functions or inheritance into a struct i wonder what mental gymnastics you would engage to explain why they shouldn't do that without using C to do so.
it's like having a debate with the iraqi information minister, you don't even believe yourself what you are saying.
C# and D are sufficiently different, D and C# will not compile most/any C programs.
as soon as any of your students got in to a professional context and have to use C++ in anger, they found out pretty quickly that they actually didn't understand it. you did them a disservice by ignoring C.
the design decisions in C++ are reactions to C - not computer hardware, architecture or assembly. there are a few exceptions to this, but they are small in number.
the "why" is staring you in the face and you are willingly ignoring it. it's actually infuriating to try and learn from someone with this attitude, never explaining why correctly but giving directions with no explanation or falsified explanation.
>Use struts when you just need a data container. Use classes when you need to add behaviour. Simple rule,
> A struct is a C++ data type that offers the same set of features as a class, just with a public access as default.
these are lies. at this point it's a semantic argument, what you mean by "understand" is "use". what i mean by understand is its dictionary meaning.
to answer your questions, no you don't need to explain them or teach C to them because the syntax for struct in those languages is not identical to C. that is why. because they are not C structs. "C++ structs" are C structs with some syntatic sugar applied. C structs will not compile in those languages. C structs are not valid code in those languages.
I am aware of the long list of minor incompatibilities between C89 and C++, and how C++11 has removed some and added more
In short you know nothing about this I don't already know and you are quite happy to knowingly deceive your students. i object but there's no point in debating with you further.
Linus has ported his Subsurface project to Qt, thanks to the current state of affairs in Gtk, so I imagine he might be a bit more welcoming to C++ nowadays.
A major problem with C++ is a lack of consistency, which you run into if you start trying to generalize the ideas presented in this article.
The article demonstrates string concatenation using +. That is great! Except it's inconsistent. The article's example works fine, of course:
return name+'@'+domain;
I'll skip over the weird use of '' instead of "". Now let's say I want to prepend mailto: as well:
return "mailto:" + return name+'@'+domain;
So far so good. But I think it might be clearer if the colon was separated out:
return "mailto" + ":" + name+'@'+domain;
Oops, this no longer compiles. Hey beginner programmers, let's take time out from the arduous task of learning basic programming to understand what a const char * is and how it differs from const string and why you can + two strings or a string and a const char * or a const char * and a string but you can't + two const char *s.
The next example demonstrates initializing a vector:
vector<int> v = {1,2,3,5,8,13};
This is great, of course. The example after that introduces "auto" so you don't have to write the type of a variable. Well heck, let's combine the two!
auto v = {1,2,3,5,8,13};
Kaboom. Oops. You can use auto, or you can use {} to make a vector, but you can't do both at the same time! OK, beginners, let's take some more time out from learning what you came for and instead learn about the complex machinery that handles initializing a custom type with {} and why "a = b" can do arbitrarily complex things depending on the types of a and b.
I had a job in college tutoring students in my CS department's first-semester programming course, which was taught in C++. People would routinely come in with code that wouldn't compile because of some tiny mistake, but which produced literally pages of error output. There was no way for new students who were still struggling with the concept of a loop to figure out what they were doing wrong, besides finding somebody who had already been through it.
This was a long time ago, and C++ has improved, especially in the error message department. But these problems are still there, even if somewhat diminished, and other languages don't have them.
The fundamental problem with C++ is that it grew organically from humble beginnings without any apparent plan. This allowed to adopt a lot of nifty features and become extremely powerful, but it also means that there are a ton of bizarre corner cases and inconsistencies to deal with. For many projects, the tradeoff is worthwhile. But it's one of the worst choices imaginable for teaching new people to program.
> Kaboom. Oops. You can use auto, or you can use {} to make a vector, but you can't do both at the same time!
IMO it would be more confusing if that created a vector<int>. Unless the compiler is reading your mind, how is it supposed to know you meant a vector<int>, a set<int>, deque<int>, a struct { int a,b,c,d,e,f}, or some other object who's constructor can take 6 ints?
The way most other languages handle it is that the list literal construct just creates a list, and if you want something else then you need to explicitly convert. For example, in Python:
x = [1, 2, 3, 5, 8, 13] # this is a list of integers
y = set([1, 2, 3, 5, 8, 13]) # this is a set of integers
There's nothing inherently wrong with C++'s initializer list approach. Nor is there anything inherently wrong with auto. But they collide in unfortunate ways because they haven't been designed such that they go well together. C++ is full of things like this. Responding with "well, that makes the most sense" for any given feature is missing the point entirely.
> The way most other languages handle it is that the list literal construct just creates a list, and if you want something else then you need to explicitly convert. > For example, in Python:
> x = [1, 2, 3, 5, 8, 13] # this is a list of integers
> y = set([1, 2, 3, 5, 8, 13]) # this is a set of integers
Actually, that's not the best way to do it:
y = {1, 2, 3, 5, 8, 13} # this is a set of integers without creating a list first.
z = set(1, 2, 3, 5, 8, 13) # so is this
In C++ {} is syntactic sugar for "Create an initializer_list with these values," which is what happens when it's assigned to an "auto" variable. Not surprising at all. If you want a different type, you need to specify the type that you want.
Maybe it's confusing to beginners and people who don't take the time to learn the language, but C++ isn't catering to those people.
Don't get me wrong, there are a lot of "gotchas" in C++, I just don't think the two you've posted are very bad.
Well, to be fair, with "auto x = { 1, 2, 3, 4, 5, 6}" you will get a variable named "x", but it won't be a std::vector<int> (it'll be a std::initializerList<int>).
where TYPEHERE is the type of array[1] that you're creating. Is there any equivalent C++ technique here? Explaining all the machinery may blow out a novice's mind, but I think explaining "You need to give C++ some clue about the type" isn't so bad.
[1]: Before you complain I just created a slice, remember the slice has to point at an array; I've created both a slice and an array so it's not "wrong" to say I've created an array with that syntax.
To be fair everything you're upset about is a problem with the standard C++ library, not the language itself.
Not to say that the language doesn't have issues. The initializer list example you provide is half of one, but only because people don't like to write algorithm-based code.
This is actually fine:
auto v = {1, 2, 3, 5, 8, 13};
for (auto i : v) {
std::cout << i << '\n';
}
...all that being said, the C++ standard library gets strings very wrong. I've ranted about it here before. But, to be fair:
1. What language gets strings right?
2. C++ has some interesting ideas around the bend (concepts) that should make using library code much easier. Whether writing library code will be easier is another question, but I'm hopeful.
The standard library is part of the language. But that aside, the fact that + doesn't work on string literals cannot possibly be a problem with the standard library. The fact that the language lets you extend + to work with some types but not others is not a problem with the standard library.
As for initializer lists, the problem isn't simply that {} doesn't produce a vector, it's that identical-looking constructs do completely different things in different contexts. a = b can do completely different things depending on where you see it and what declarations are visible and on and on. Again, not a problem with the standard library.
The standard library suffers from these problems, as it must since it exists within the language that causes them. But the problems are not in the standard library.
> a = b can do completely different things depending on where you see it and what declarations are visible and on and on.
I don’t see your problem with this. "a + b" certainly can and should do "completely different things depending on where you see it" and "+" is commonly used in completely different contexts in real-world usage, too, e.g. integer addition, integer addition modulo x, real number addition, complex number addition, vector addition, matrix addition, vector space addition, set addition/union etc. etc.
To restrict "+" to only act on integers (or reals?) and require a +_real, +_complex, +_vector, +_matrix etc. seems unreasonable.
Now, given that "+" may well do whatever it seems fit with its two operands, why should "=" not be allowed to do the same?
Standard libraries are part of the language. I would go so far as to say anybody who designs a language that isn't meant to make their standard libraries powerful, consistent, and elegant isn't doing any favors for the discipline.
C++ is a bit different in the variety of domains that it's applied to. To expect the same standard library to be useful in games, microcontrollers, medical instruments, aircraft control systems, kernels, and CRUD apps is a huge ask.
Maybe you don't like that, which is fine. But C++ is much broader than what's available in the standard library. C++ is a close second to C in the "how optional is the standard library?" contest.
>>The next example demonstrates initializing a vector:
>> vector<int> v = {1,2,3,5,8,13};
>>This is great, of course. The example after that introduces "auto" so you don't have to write the type of a variable. Well heck, let's combine the two!
>> auto v = {1,2,3,5,8,13};
The auto example creates an std::initializer_list which then can be converted into a vector or a set
What he has in mind is that it is logical and reasonable for C++ to behave in this way, but completely incomprehensible for beginners who has just learned the basics of programming.
The aim of C++ is not to be accessible to beginners. There are other languages for that. The philosophy of C++ is to give you power and it's up to you how to handle it.
The list initialization syntax touted by Bjarne Stroustrup himself as beginner friendly is not even friendly to advanced users. Tell me how to debug or teach students the difference between
I just compiled the C code and the C++ code using:
According to valgrind: Using gcc (Debian 4.7.2-5)[edit] Repeated using gcc 4.9.1 and clang 3.5.0 with -std=c++14. No difference.
[edit] Seems with Clang and libc++ it's better than the C implementation as it doesn't do any heap allocation:
But still, that's only because the C version was explicitly written to use heap allocation. It need not do so.