Poor Articles And Falsehoods About C++
September 6, 2006 11:53 am C++ Myths, C/C++, ProgrammingIt’s never been entirely clear to me how articles like this article on linuxdevices.com get cleared to be published. If they’re onced over by someone with even half a clue about C++, they’d just get turned down. It makes me worry about the quality of the magazine publishing them. Nonetheless, someone used this article last night to justify a stance borne largely in ignorance as regards C++, so it’s time to clear the air about the language. Today, I start a new category to curb C++ naïveté and misapprehensions. This is the first entry in “C++ Myths.”
The list of falsehoods in this article is both startling and borderline offensive to the adequate programmer. I’ll just copy them as a list and respond to them individually.
- C++’s virtues are expensive. Advanced OOP features, such as templates and the practice of using classes in the place of primitives, to name two examples, cause unacceptable code bloat.
- Malarky.
- Using classes in the place of machine types (there’s no such thing as a “primitive” in C or C++, you Java wank) has exactly zero overhead by design, except in those cases where one has RTTI turned on, which one should not for embedded. Classes are structs and structs with a single member are, as in C, equivalent in storage to said member. This is a requirement for C/C++ to match structs to hardware registers.
- Using templates doesn’t cause massive bloat. Templates are only instantiated for the types in which they are used. For classes and structs this generates zero overhead without virtualizations, and one vtbl per finalized type with. In either case, the comparison to a parallel C implementation requires the same functions to get filled out and used; this means that C ends up with at least as much binary, just hand-made. Furthermore, C++ (since it knows what it’s up to) can frequently share large parts of, and occasionally the complete implementations of, said functions, meaning the C++ implementation – along with being shorter, cleaner more maintainable and always correctly linked code – is also frequently binary smaller.
- A C++ compiler may generate many routines for one function (templates) or create routines where no function explicitly appeared (constructors, casts, etc.). There is generally a one-to-one relationship between a function in C code and the resulting machine-code routine. It’s easier to optimize what you can see than what you must infer.
- Empty constructors are optimized out during link. You might as well complain that C++ has constructors for integers. They do not exist in the binary, take no space and are never called. There is just as much a “one to one relationship” between C++ code and binary as there is with C, if you know what the code actually says. Complaining that the language considers every type to have a constructor, when it gets optimized out and has zero impact, is just silly. Wah wah, the language wants to be uniform and not have special cases that cost nothing. The sky is falling and so is my soufflé.
- The notion that there being a one-to-one relationship between your code and your binary output is either rare or significant is just absurd.
- “It’s easier to optimize what you can see than what you must infer.“
- You don’t need to optimize things with no cost.
- Premature optimization is the root of all evil stupidity.
- To infer suggests that you had to do some detective work to ferret out that this is being done. Sure, if you don’t know C++ you might have to infer some things about it. This is equivalent to someone complaining about discovering the branch overhead of calling a function in C, because they didn’t know the language and didn’t know it’d happen. You’re complaining that a language you don’t know does things you don’t know about. Boo, hoo: read a book, Charlie.
- Virtual methods and polymorphism complicate runtime linking and require many relocations. This slows C++ application launch time considerably. C applications are both simple to link and amenable to lazy linking, so they load quickly. (For details, see Waldo Bastian’s paper “Making C++ Ready for the Desktop”, http://www.suse.de/~bastian/Export/linking.txt.)
- No.
- Virtual methods and polymorphism allow runtime linking. In C, you can’t have runtime linkage, unless you roll it yourself, and if you roll it yourself you’re just reinventing what C++ did. Given that at runtime you don’t know as much about the nature of the code as the C++ compiler did, you are guaranteed to be unable to do as good a job as C++ does while remaining correct (read: safe) and portable.
- Neither virtual methods nor polymorphism have anything to do with relocation.
- Relocatable code is code which is compiled to an address offset rather than an address. This is useful for situations in which code is to be loaded at an unknown address, such as with dynamically linked libraries, static objects, and pretty much anything in a multithreaded environment. If code is relocatable, then you can just load it at whatever address has space available, set the offset to that address, and wham, everything works.
- Thing is, this isn’t how things work everywhere. An embedded programmer should know this. Non-relocatable code is compiled to a fixed address. This is marginally faster, as it skips an addition during lookups and function calls, but that’s not tremendously important. With relocation turned off, as is the case in most embedded environments, polymorphism and virtual methods still work just fine. Ask anyone who writes games for a console video game system; because the programmer is in complete control of the machine, relocation is generally unnessecary, and so is generally disabled, but we still use polymorphism and virtualization extensively.
- Nothing ever requires multiple relocations. It is contrary to the concept of location in a single dimensional memory space. This is just retarded.
- Relocation has zero impact on “C++ application launch time.” Relocation is used in any application written for Windows, including not only C applications but also windows assembly applications. Relocation has identical impacts on those applications as on C++ applications. The impact that relocation has on the launch time of general applications is trivial: it’s the writing down of one single solitary offset. It’s an allocation, a lookup and a write. On most machines, it’s fewer than ten cycles. You wouldn’t notice the impact on a computer from the 1950s. Get over yourself.
- The simplicity of linkage and the concept of lazy linkage have no effect on application load times whatsoever. Linkage is a purely compile-time phenomenon. None of the things done by the C++ runtime have anything to do with module linkage, nor with “runtime linkage.” It is important to realize that the reason C++ doesn’t call it runtime linkage is that it isn’t equivalent in any way to what the compile-time linker does; that alternative name is an artifact from languages which are completely dynamically linked, such as Smalltalk, and shows a deep confusion about the actual nature of compilation.
- C++ is just fine for lazy linkage, which is used extensively by most large C++ projects to reduce compile time. C and C++ linkage are identical, other than C++ name decoration. Anything the C linker can do, the C++ linker can do. Their definitions in the standard are identical, other than name decoration rules and the ABI section that deals with passing the this pointer
- Each class with virtual methods has an associated vtable array, which adds memory overhead.
- So?
- If you have a hundred classes with virtual methods, and each one has an average of three virtual methods, on a machine with 4-byte pointers that’s a walloping 1600 bytes. Those vtbls are only made for virtual methods when there are multiple implementations of the method, and only when the class method or member is called through a base pointer; this overhead only exists if you’re actually using it.
- This means that the only situation in which you could do it by hand in C in less space is on a processor like the ARM, where by switching to Thumb you could write smaller code than the compiler is inclined to generate. Overhead doesn’t exist in a vacuum: it’s only there if you can do the same thing in C in less space. In fact, given that in C you’re going to have to manually branch to a locally stored address, the C implementation is highly likely to end up larger.
- Besides, if you’re in an environment where 1.6K makes a difference, you’re not going to have a hundred classes, each with three virtual members. This is a “problem” which just doesn’t occur in the real world, even in the embedded world, at any scale.
- C++’s tighter type-checking makes it difficult to write the space-conscious code reuse common to C applications (think: void *).
- What the hell? A void pointer works in C++ exactly the same way as it does in C, with the exception that you have to make all casts explicit in code. Given that that has exactly zero binary impact, I can’t imagine why anyone ever believed this was true.
- Even so, C++’s tighter typechecking actually means space-conscious code reuse is much easier and much more space efficient than C’s is, since you can use a single implementation and rely on stronger conversions through the OCR.
- If you don’t know that the OCR is the One Conversion Rule, you’re not qualified to discuss typal issues in C or C++.
- The small, simple code demanded of embedded projects provides maintainability. There is no reason to assume OOP will further simplify such systems.
- Yes, C++ is bad because you don’t understand it well enough to see a benefit coming a mile away. (cough) This kind of hubritic nonsense even looks out of place on the internet. Hang your head, shame-boy.
- The specific purpose of objects is to provide local simplicity and maintainability through modularity and maintained interfaces. This is explained in the first two pages of The C++ Programming Language. It’s obvious you haven’t read the book. Funny how you still feel empowered to criticize something you plainly don’t understand…
- GUIs may not have a simple solution in a rigorous OOP model.
- Have you ever used an object-driven GUI system? GUIs are the poster child for how objects can make life easier. Essentially every GUI in existence is built on an inheritance model. Granted, most GUIs provide a C interface because C++ linkage isn’t nearly as universal; that doesn’t mean it’s any less OO.
- Whether GUIs have a solution in an OOP model isn’t germane.
- GUIs have no better solution in C than C++.
- GUIs are rarely a concern in embedded development. Do not confuse developing Linux applications for a small machine you hold in your hand with embedded development; embedded development means to the bare metal, without an operating system, typically in ROM. Most embedded applications have no interface at all; they control things like your anti-lock brakes, the temperature coming out of your air conditioner and whether that key card just unlocked that door.
- There are literally dozens of GUI libraries available to C and C++. That the language explicitly chooses not to implement them is a tip of the hat to that real embedded developers generally cannot use the kind of library that most people would want in the PC arena. That C++ chooses to skip GUIs is in fact a huge win for embedded, not a loss. An embedded C developer should know that, as it has the exact same win.
- This is absurdism coupled with naïvété, plain and simple.
- It’s easy to get carried away and start doing OOP for OOP’s sake. The One True Object Model may describe a problem perfectly, but it comes at the cost of excessive code.
- Yes, it’s true that bad programmers can get stuck on a near-religious devotion to a language feature. If you’re choosing languages so that there are not useful features, to prevent your programmers from doing stupid things, you need to start hiring better programmers.
- You might just as well suggest that C is not an acceptable language because some programmers get wholly stuck on implementing a system as a huge ball of function pointers, and that all embedded should be assembly.
- Carefully written C code can be much faster than C++ code, especially on embedded hardware. GTK+’s hand-crafted object system offers much better spatial locality than C++’s more numerous and distributed constructors. Our device has a tiny cache, so locality is an especially important performance consideration.
- There is no reason for you to suggest that C can be much faster than C++, and time and time again benchmarks prove that this is not just false, but backwards. The C++ compiler knows more about its code than does the C compiler, and therefore can establish that some things (notably sub-const typal concerns and volatility issues are a big deal here) are safe when the C compiler wouldn’t be able to. As a result, the C++ compiler can make more aggresive optimizations than can the C compiler.
- GTK’s hand-crafted object system exists as a legacy system to support C and ASM applications, just like Windows’. It doesn’t offer better spatial locality than C++ GUI systems would; it uses the same malloc that GCC’s new is implemented with. In fact, a C++ GUI can provide better spatial locality than a C GUI can, because of things like overloading operator new, which allows the reuse of code segments that C would have seperated. On embedded machines, where ITCM caches are frequently 8-32k, that space savings can be significant.
- “C++’s more numerous and distributed constructors“
- … FUD much? Unused constructors have 0 binary impact, and used constructors are guaranteed as small as or smaller than their C implementation counterparts. The count thereof is unimportant, as they’re only going to be there if they’re doing things, and so in the C parallel implementation a minimum of the same count of things will be implemented.
- Distributed constructors? In the words of Seth McFarlane, “can I buy some pot from you?” They’re just functions. They’re kept in the .code segment just like everything else.
- “Our device has a tiny cache, so locality is an especially important performance consideration.“
- So co-implement operator new, which will save you space on things with construction behavior. Things without construction behavior won’t be generating any constructors anyway.
Anyway, that’s that for this particular list, but lord knows I’ve found enough other articles like this spreading misapprehension and outright falsehoods about C++. Maybe, with time, I’ll clear up some others, too.

September 7th, 2006 at 7:27 pm
What’s the ‘One Conversion Rule’?
I can’t find anything about it anywhere.
September 11th, 2006 at 3:21 am
Under some circumstances the compiler is allowed to perform exactly one type conversion.
For example, from const char * to std::string because std::string (actually std::basic_string, but we all knew that) provides a constructor that takes a single const char * argument and that constructor is _not_ marked explicit.
April 23rd, 2007 at 1:38 pm
I believe the “one conversion rule” limits the number of user-defined conversions the compiler will do. It will, for example, allow conversion from a char* to a const char*, then apply a user-defined conversion from const char* to Foo. However, it won’t then allow implicit conversion from that instance of Foo to Bar, or whatever other user-defined conversions exist. These must be chained manually. The compiler only allows one user-defined conversion in a conversion chain.
March 14th, 2008 at 12:27 pm
Really glad I found your blog. Excellent blog. Easy to follow step by step instructions which seem to be absent from every other site dealing with C++ Programming.
Keep it up.
December 8th, 2008 at 1:35 am
Thank you. I would never have had the energy to do all those corrections myself.
Maybe it’d be a good idea to put your article on
http://www.hoaxbusters.org/ ?
Wrong ideas about C++ are so common that they qualify as a “Internet hoax”.
December 8th, 2008 at 11:40 am
A Google search for
OCR “one conversion rule”
got 3 hits. One of them was this article; the other two happened to use the phrase near a reference to Optical Character Recognition.
Is this rule stated in the C++ standard? Perhaps there’s another term for it.
And perhaps you should reconsider your statement: If you don’t know that the OCR is the One Conversion Rule, you’re not qualified to discuss typal issues in C or C++.
December 8th, 2008 at 3:26 pm
Thank you for your efforts on debunking these myths about C++. It is sad to see how much FUD exists out there and how little some people understand the intricacies of language design.
I hope you keep up with the task because these articles keep popping up (what else could you except when even Linus spreads such misinformation…).
December 8th, 2008 at 3:52 pm
Mr. Thompson: yes, the one conversion rule is in both the C and C++ standards. It’s literally the very first thing in Chapter 4, where it’s known as the zero or one conversion rule (almost nobody says the “zero or” part, which is only enforced with keyword explicit).
Look in the part of the standard called, in the ToC, the “standard conversion sequence”.
December 8th, 2008 at 3:52 pm
Prasinos: thank you for the kind words.