Errors From Another Planet–The One Thing I Hate About C++


There is a lot of C++ hate flying around these days. Seems everyone has a snarky comment to make about my favorite language. I chalk this up to developers being raised on Fisher-Price-toy languages like Javascript and a lack of appreciation for coding closer to the hardware (a.k.a. understanding how computers work, not just how the code works). Realize, I’m not talking about preferring another language here. I’m talking about being prejudiced toward the language.

I once had someone tell me whoever wrote C++ must have been completely insane. No, in fact Bjarne Stroustrup strikes me as exceptionally down-to-earth and listening to him speak and reading his books is a pleasure. I challenge anyone to point me to a language that was developed after C++ that does not borrow something from C++.

That out of the way, there is one thing I absolutely hate about C++: Compilers.

If there is anything that should scare people away from the language, it is the ridiculous gibberish C++ compilers vomit on the screen when they don’t understand something you wrote. I remember being a teenager and trying to write video games while teaching myself the standard template library. If I typed one thing wrong using an STL container, boom, 1,500 lines of templated mishmash that has nothing to do with what I did wrong. Seriously, C++ compiler errors look like hieroglyphic roadmaps to an alien world.

Lately I’ve been working on a synchronization and scheduling routine with a fairly deep inheritance tree that I originally wrote in C#. (I can hear the C++ haters now: “Why are you rolling your own ‘synchronization routine’ when you can just use One-Size-Fits-All-Framework and Snazzy-Hipster-Design-Pattern?!” Come back and throw solutions at me when you understand the problem, son.) C# is a fantastic language, and it was fine for my fluffy application version of the software. Make classes, throw references around all over the place, let the garbage collector take care of it. However, the embedded devices were written in C++, and I have no desire to deviate from that. It gives me much more granular control which, if I play my cards right, works to my advantage in such a purpose-built system.

I write up 90% of the shell of the thing, then this happens:

/usr/include/qt5/QtCore/qlist.h:431: error: use of deleted function ‘Media::MediaFile& Media::MediaFile::operator=(const Media::MediaFile&)’
mediafile.h:17: error: use of deleted function ‘BasicFile& BasicFile::operator=(const BasicFile&)’

I can’t use a function that I didn’t write because it was deleted? O_o

If I click the first line, it takes me to the source for QList here:

#if (defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__IBMCPP__)) && !defined(__OPTIMIZE__)
    // This violates pointer aliasing rules, but it is known to be safe (and silent)
    // in unoptimized GCC builds (-fno-strict-aliasing). The other compilers which
    // set the same define are assumed to be safe.
    else *reinterpret_cast<T*>(n) = t;

Okay, but I don’t do anything with QLists in any of those files? And thus marks my favorite part: The problem isn’t actually in any of those referenced files. After hours and hours of digging I find the trigger in a completely different file where I iterate over a QList of MediaFiles with a range loop:

for (auto mediaRef : mediaRefs) media.append(Media::StandardMediaFile(mediaRef));

So now what? Google searches lead me to believe I need to loop by reference, so I add auto &mediaRef. Nope. More Google searches tell me I need a copy constructor. So I write one. Then another for the base class. Then another for the base of the base class. Nope, no joy. What on earth is causing this error?!

I start Google searching on the whole “deleted” concept which leads me to pages of gibberish explaining the compiler gibberish written in the same gibberish the compiler is speaking in and I get even more off-track and knee-deep in the gibberish. In a nutshell, special functions like copy constructors, which the compiler automagically generates, are deleted when certain criteria can’t be met.

Great, compiler spam and automagic, and I can’t determine from the technical articles written in alien what the criteria is.

I revisit this weeks later and hark! I’m still chasing ghosts. The function operator= is not a copy constructor, it’s a copy assignment operator! My bad. So, if the compiler deleted it, I just need to write it, right? So, I write one:

BasicFile& operator=(const BasicFile&);

Nothing.

Hours and hours of research and failed guesses later, I happen across an interesting comment on Stack Overflow:

Unexpectedly, gcc 4.7.2 produced its usual vomit of incomprehensible errors when I tried this, but clang at least made the error readable and clang with libc++ compiled it with no errors.

Hmm… I’ve avoided clang up to this point. I’m of the mind, “Why do I need clang if I already have gcc?” But you know what, let’s try it! I switch my compiler to clang and manage to only get misdirected once before finding the gold nugget:

./basicfile.h:30:16: note: copy assignment operator of 'BasicFile' is implicitly deleted because field 'LOG_PREFIX' has no copy assignment operator
        const QString LOG_PREFIX="FILESYSTEM";

Ermagherd! It’s what I’ve been looking for from the very beginning. It’s the reason the copy assignment operator was deleted! That makes sense. A class with a const member isn’t copy assignable! I take the const off and it builds!

Phew. Madness.

I hate compilers. I may love what they do, but I hate every one of ’em.


Leave a Reply