|
|
|
Sponsored Link •
|
|
Advertisement
|
Nonetheless, there are certain applications I wouldn't dream of writing in Python (or in Java or C#, for that matter). When efficiency is an issue, and you need ultimate flexibility, C++ is the only object-oriented (multi-paradigm, even) game in town. That's certainly one reason it's still so heavily used. That's why I was summoned to Symantec teach it this summer. Their C++ developers do heavy lifting that the Python folks couldn't begin to do. I don't need to convince this audience.
Einstein's maxim, "Solutions should be as simple as possible, but no simpler," is worth pondering here. If a language is too simple, it will lack the power needed to craft industrial-strength applications (try solving systems of differential equations efficiently in Forth or Visual Basic). Any tool or discipline rich enough to solve any software problem within real-world resource constraint parameters is probably not "too simple." Likewise, any such tool or discipline will likely come with a steep learning curve, just like non-Euclidean geometry is as easy as it's ever going to get [2] :-).
Since my wife is a hand weaver, comparing languages to looms readily comes to mind. A loom has warp threads running vertically from the weaver's point of view. These threads are attached in varying configurations to a number of shafts; lifting a shaft lifts its attached threads, so when a horizontal (weft) thread passes through, it interleaves between the threads lifted by the shaft and those that are not, exposing only part of the weft thread in the finished result. The complexity of a fabric's overall design is proportional to the number of shafts in the loom; the more shafts, the richer the result. Beginners typically start with toy looms with two shafts, and then progress to four, eight, and so on. To implement an interesting design on a loom unassisted by computer or Jacquard attachments [3] requires sixteen or twenty-four shafts. The effort required to effectively use such looms is great, but the result is worth the effort and simply unattainable by more elementary looms.
In the world of computer programming, C++ is a 128-shaft loom.
But is C++ as simple as possible? It depends on what you think is "possible." I once asked Bjarne Stroustrup what language he would have designed if C compatibility were not an issue, and he let me know in no uncertain terms that the question was not a fair one. He was right, I suppose. I still hear it said that there is too much investment in the C/C++ relationship for these languages to "break up" now, but I wonder if it is time to revisit that assumption. (I personally wouldn't mind if they parted ways—my C days are far behind me; your mileage may vary). Even if the bothersome noise in C++ due to C compatibility isn't going away anytime soon, is there still some room to considerably simplify things anyway?
When asked to name three things he disliked about C++, Scott Meyers said:
"I'd like to answer this question with 'complexity, complexity, complexity!', but naming the same thing three times is cheating. Still, I think that C++'s greatest weakness is complexity. For almost every rule in C++, there are exceptions, and often there are exceptions to the exceptions. For example, const objects can't be modified, unless you cast away their constness, in which case they can, unless they were originally defined to be const, in which case the attempted modifications yield undefined behavior. As another example, names in base classes are visible in derived classes, unless the base class is instantiated from a template, in which case they're not, unless the derived class has employed a using declaration for them, in which case they are." [1]
I must admit that one of my greatest frustrations in teaching C++ is all of the arcane twists and exceptional cases. Consider the following "gotcha" that occurs in a beginning course. When introducing templates, you want to keep things simple, so here's a class template that holds a single value:
template<typename T>
class Box {
T value;
public:
Box(const T& t) {
value = t;
}
};
Now try to introduce a stream insertion operator as a friend in the usual
way:
template<typename T>
class Box {
T value;
public:
Box(const T& t) {
value = t;
}
friend ostream& operator<<(ostream&, const Box<T>&);
};
template<typename T>
ostream& operator<<(ostream os, const Box<T> b) {
return os << b.value;
}
Students expect to be able to do this as they do for non-template classes.
But wait a minute. Is operator<< here a template or
not? And do I want it to befriend all specializations of Box
or not? No conforming C++ compiler will let you instantiate the
Box class and use the associated stream inserter. I think
g++ gives the best diagnostic for this error:
box.cpp:11: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const Box<T>&)' declares a non-template function box.cpp:11: warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)This message suggests the following correct solution:
// Forward declarations
template<class T> class Box;
template<class T>
ostream& operator<<(ostream&, const Box<T>&);
template<class T> class Box {
T value;
public:
Box(const T& t) {
value = t;
}
friend ostream& operator<< <>(ostream&, const Box<T>&);
};
template<class T>
ostream& operator<<(ostream& os, const Box<T>& b) {
return os << b.value;
}
An alternative solution defines the operator friend function in situ:
template<typename T>
class Box {
T value;
public:
Box(const T& t) {
value = t;
}
friend ostream& operator<<(ostream& os, const Box<T>& b) {
return os << b.value;
}
};
The first approach requires forward declarations and a very unusual syntax
in the declaration for operator<<. The second, while
simpler, is not exactly equivalent to the second, because
operator<< is not a template in that case
[4].
Do you think this bewilders beginners? You bet it does—and quite a few veteran developers as well.
No, I don't think C++ is as simple as it could be. I love templates, I love the flexibility of C++, but I too often lament lost brain cycles tracking all the gotchas, especially when teaching. Speaking of PL/I, Dijkstra remarked
"I absolutely fail to see how we can keep our growing programs firmly within our intellectual grip when by its sheer baroqueness the programming language—our basic tool, mind you!—already escapes our intellectual control." [5]Food for thought. My current leaning is to use Python when I can, C++ when I must (which is still often and usually enjoyable) [6], and to be ever on the lookout for a language that gives most of the power and flexibility of C++ but loses most of the headaches. Finding one is not going to be easy [7]. Here's hoping that the standards committee will do all it can to make using C++ as enjoyable as the incredible software we create with it.
|
Sponsored Links
|