C++ is bad: problems with the ternary operator, addendum

I’ve found another interesting wrinkle in the ternary operator. So, here’s a bonus question for that quiz from last time. You start with the following code:

class Abstract;
class DerivedOne;  // Inherits from Abstract
class DerivedTwo;  // Inherits from Abstract
Abstract x;
DerivedOne one;
DerivedTwo two;
bool test;

Again, you can assume that all of these are defined/initialized (and you can assume the comments are correct—both Derived* classes inherit from Abstract). Now, consider these snippets:

# Code Snippet
A
Code Snippet
B
Bonus
if (test) x = one;
else x = two;
x = (test ? one : two);
 


If you read the previous installment, you’ve probably guessed by now that these are not equivalent, and you’d be right. In what ways do they differ?

Do you have an answer? Better double check that.

 

Alright, time’s up. It’s a little bit of a trick question. Snippet B does not compile! Instead, it fails with an error: no match for ternary ‘operator?:’ in ‘test ? one : two’ (at least, that’s what it says in GCC; your error message may be different). Section 5.16 of the C++ Standard states that the ternary operator will only work if, of the two values that can be returned, one is a subclass of the other (or one is an exception that gets thrown, or a couple other things like that). In other words, snippet B is not valid because DerivedOne is not a subclass of DerivedTwo or vice versa, regardless of whether they share a superclass. As before, I feel that this goes against the Principle of Least Surprise, and it bugs me.

Leave a Reply

5 Comments

  1. janna says:

    I think this whole concept is kind of interesting. I was going to comment that of course NEW languages have smoothed things out. C++ has been built up over so many years that its bound to have quirks, and quirks it can’t get rid of because you’d be pretty pissed if it suddenly started working differently…

    But then I’m thinking about any kind of software… and how for example at my company every version is basically required to be backwards compatible. It’s a nice idea, but it means that an error in our API in version 1 will persist even when we know its bad. Our customers would be upset if we changed the behavior… as would many C++ customers probably be if C++ was “fixed”.

    So that leads me down an interesting road: In order to have *the best* software, do we need to abandon the idea of supporting older versions? To me, its pretty much what you’re doing by switching to a newer language… they’ve smoothed out the idiosyncrasies of C++ so you abandon the backwards compatibility in favor of what is cleanest now. But what will you do in 5, 10, years? Will you rewrite all of your code in the newest language, or will you deal with what may seem like oddities of python/java? How do we determine the right balance of support for old and moving forward?

    Also, out of curiosity, what are your feelings on C#?

    • Alan says:

      I couldn’t agree more! Dealing with legacy bugs like that is a tricky issue. I feel lucky that everything on which I work runs on our own servers; if there’s a problem/bug/inconsistency, we just fix it, notify everyone who needs to be told, and move on. It seems like many of our systems get completely rewritten every couple years, so whatever bugs we had disappear in the new version.

      Microsoft, in contrast, has made the opposite decision, and I think it’s hurting them. Their software is convoluted and buggy because the old versions were convoluted and buggy, and they want to retain their backwards compatibility because there are so many pieces of third party software that rely on those bugs. and since they refuse to fix these things and break backwards compatibility, they have all sorts of security problems and their document formats are convoluted, arcane, and should never have been passed by ISO. :-) Then again, they can’t really do it another way—the flag day in 1984 when everyone switched from using NCP to IPv4 was a disaster. Getting this right is a very hard problem.

      Perhaps a better way to do it is something like the model Firefox uses for their plugins: each plugin must specify with which versions of Firefox it’s compatible, and if you upgrade Firefox and your plugin is no longer compatible, you need to either downgrade again or update the plugin (or not use the plugin any more). This puts the burden of maintenance on the plugin developers, which is great for everyone except certain users who rely on plugins that are no longer maintained.

      I don’t know enough about C# to judge it. It’s certainly better than C++, but it still feels too low-level for my tastes (optional garbage collection, for instance). I would much prefer a very high level language that abstracts away all the individual bits and assembly instructions. but you sound like you know much more about it than I do. What are your thoughts?

      • More reasons not to use the ternary operator!

        How high-level do you want to go? Scripting languages are wicked slow (no matter how much we love Python), and dynamic typing really is a bad idea for big projects. The only other thing to do is take a step sideways, and try to live your life in {lisp, ocaml, haskell}, which introduces an entirely different set of issues.

        Spolsky has a nice discussion of Microsoft’s issues: http://www.joelonsoftware.com/items/2008/02/19.html

        • Alan says:

          That is a nice discussion of the MS file formats.

          As for how high level I want to go, I spent a fair amount of time thinking about that yesterday, and even discussed it with some friends at a party I went to that evening (for the record, I was not the first to start a CS discussion at this party). Look for an upcoming post about my (and others’) ideal language.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>