Alright, I know that complaining about C++ is something of an industry, and kind of an annoying one. All things equal, I’m one of those dusty people who actually likes C++, so I don’t want to contribute to this overly much. That said, I think if there’s one thing everyone – lovers and detractors of the language – can agree on, it’s that more attention to keeping things consistent would help a lot.
I’m not one of the programming language rankings hobbyists – but I think everyone who programs has at least some informal preferences tucked away here and there, and “consistency” is one of my higher ones. It’s why I like Python so much. Sure, Python has its problems, but by and large consistency isn’t one of them. With just a few minutes of tutorial, you’re already anticipating what other constructions in the language will look like. This is a Very Good Thing. I despise Perl for the same reason. It’s just a horrible mess. There’s all flavors of subtly different idiom for doing basically the same thing; you really just wish they would make up their minds already.
With C++, the problem generally isn’t Perl’s problem of “there’s more than one way to do it.” That’s true in some cases, but the various ways aren’t so radically different that you can barely recognize them as the same language. What annoys me about C++ is more that all too often the rules change for special cases in deceptive ways.
Take today, for example. I’m sitting here reading a book I’ve read before all the way through yet again already because every time I sit down to program in C++ I find that I have to review. And the reason I have to do that is because of all these idiosyncracies. At the moment, I’m reading about static data members here in Chapter 15, and I discover that this is apparently an acceptable way of initializing one:
Class::member = value;
Alright, good, I get it. Static member data is data that’s associated with the class, so there’s something to be said for accessing it through the class name rather than the individual object name. Indeed, that’s probably the way you want to do it, since requiring initialization through a created object adds a lot of indeterminacy. It might not always be clear which object will do the initializing – so fair enough. What kills it for me is that you can still declare these static data members
private, in which case the
Class:: syntax is available once and once only for this data member. More than that, if the whole point of a
private data member is that you have to go through object code to get to it (providing a route for sanity checks seems to be the main purpose here), then this would seem like an incongruous exception. So like with so many other things in C++, the rule is “you have to go through object code to modify private data members, except when you don’t, and that would be in this one highly specific case, so we went ahead and gave you syntax for that.” Um…
The other result of this is that for public members you get two kinds of syntax:
Class::member = value;
object.member = value;
Splitting hairs here, granted, but it seems like if we’re gonna insist that static data members have their own special initialization syntax to mark the fact that they’re members of the Class rather than associated with any particular Object of that class, then it really wouldn’t hurt anyone to require this syntax in all cases? I mean, why allow the second form at all? Well, one could say, because there might be situations where we don’t necessarily know the class of the object because we have several classes all with analogous public static members and it might be any one of them that needs it updated at this point in our program. Alright, so fine, that’s a good point – but if we’re going to allow the somewhat misleading
object.member = value; syntax, then why enforce the special exception that the
this pointer won’t be available in such situations, dealing, as we are, with static members? That is, in cases where
member is a function rather than a data object, then it seems like
this should be the sort of thing that can be determined by the calling object. I mean, if you have to allow misleading forms, then the very least you can do is grant those forms their normal functionality where possible, right? Alright, well, I suppose the answer to this one is that the fact of the function’s being static means that you should be able to call it without reference to any particular object. Allowing for
this pointer use inside would do some violence to the definition of “static.” Again, true enough. But here’s where my real beef with the language comes in. Viewed properly, this is yet another argument against allowing the
object.member syntax for static members in the first place. If they’re not object members and don’t behave like object members, why are we allowed to treat them as if they were for purely syntactic purposes? Alternately, if our argument is that we expect the programmer to be intelligent enough to remember the differences in functionality between static and object class members, then why bother offering the
Class::member syntax at all? It seems to me that there are two worldviews about static members trying to coexist in the same syntax. Either static members are something wholly different and should be marked as such, or there is one syntax that covers “access to things associated with classes.” Instead, what C++ opted for was something like “static members are wholly different and should be treated as such, but at the same time there is one syntax that covers ‘access to things associated with classes,’ and although ideally you should be able to pick one or the other according to your predilection, oops! it turns out that there’s one case where we actually enforce the choice for you.” Maddening.
Now, as I said, I like C++ in general. I like that it talks to the machine more directly, I like that it’s so organized, and I like most of all that it looks like C. I just wish some more “first-principles” kind of thought had been put into it. One can hope that the C++0x Committee will give due attention to rooting out these inconsistencies, as I really do think they are the major source of reported difficulty mastering the language. Some improvements on this front do seem to be on the agenda – delegation, for example. But other things – like so-called “uniform” initialization – just seem to smear yet more probably-not-terribly-consistent syntax on top of an already cluttered heap to no real purpose. But hey, we get range-based loops and lambda – so whatever final draft they come up with will pass cost-benefit on that basis alone.