Is it fine to customize C++? - c++

For my projects, I usually define a lot of aliases for types like unsigned int, char and double as well as std::string and others.
I also aliased and to &&, or to ||, not to !, etc.
Is this considered bad practice or okay to do?

Defining types to add context within your code is acceptable, and even encouraged. Screwing around with operators will only encourage the next person that has to maintain your code to bring a gun to your house.

Well, consider the newcomers who are accustomed to C++. They will have difficulties maintaining your project.
Mind that there are many possibilities for the more valid aliasing. A good example is complicated nested STL containers.
Example:
typedef int ClientID;
typedef double Price;
typedef map<ClientID, map<Date, Price> > ClientPurchases;
Now, instead of
map<int, map<Date, double> >::iterator it = clientPurchases.find(clientId);
you can write
ClientPurchases::iterator it = clientPurchases.find(clientId);
which seems to be more clear and readable.

If you're only using it for pointlessly renaming language features (as opposed to the example #Vlad gives), then it's the Wrong Thing.
It definitely makes the code less readable - anyone proficient in C++ will see (x ? y : z) and know that it's a ternary conditional operator. Although ORLY x YARLY y NOWAI z KTHX could be the same thing, it will confuse the viewer: "is this YARLY NOWAI the exact same thing as ? :, renamed for the author's convenience, or does it have subtle differences?" If these "aliases" are the same thing as the standard language elements, they will only slow down the next person to maintain your code.
TLDR: Reading code, any code, is hard enough without having to look up your private alternate syntax all the time.

That’s horrible. Don’t do it, please. Write idiomatic C++, not some macro-riddled monstrosity. In general, it’s extremely bad practice to define such macros, except in very specific cases (such as the BOOST_FOREACH macro).
That said, and, or and not are actually already valid aliases for &&, || and ! in C++!
It’s just that Visual Studio only knows them if you first include the standard header <ciso646>. Other compilers don’t need this.
Types are something else. Using typedef to create type aliases depending on context makes sense if it augments the expressiveness of the code. Then it’s encouraged. However, even better would be to create own types instead of aliases. For example, I can’t imagine it ever being beneficial to create an alias for std::string – why not just use std::string directly? (An exception are of course generic data structures and algorithms.)

"and", "or", "not" are OK because they're part of the language, but it's probably better to write C++ in a style that other C++ programmers use, and very few people bother using them. Don't alias them yourself: they're reserved names and it's not valid in general to use reserved names even in the preprocessor. If your compiler doesn't provide them in its default mode (i.e. it's not standard-compliant), you could fake them up with #define, but you may be setting yourself up for trouble in future if you change compiler, or change compiler options.
typedefs for builtin types might make sense in certain circumstances. For example in C99 (but not in C++03), there are extended integer types such as int32_t, which specifies a 32 bit integer, and on a particular system that might be a typedef for int. They come from stdint.h (<cstdint> in C++0x), and if your C++ compiler doesn't provide that as an extension, you can generally hunt down or write a version of it that will work for your system. If you have some purpose in mind for which you might in future want to use a different integer type (on a different system perhaps), then by all means hide the "real" type behind a name that describes the important properties that are the reason you chose that type for the purpose. If you just think "int" is unnecessarily brief, and it should be "integer", then you're not really helping anyone, even yourself, by trying to tweak the language in such a superficial way. It's an extra indirection for no gain, and in the long run you're better off learning C++, than changing C++ to "make more sense" to you.
I can't think of a good reason to use any other name for string, except in a case similar to the extended integer types, where your name will perhaps be a typedef for string on some builds, and wstring on others.
If you're not a native English-speaker, and are trying to "translate" C++ to another language, then I sort of see your point but I don't think it's a good idea. Even other speakers of your language will know C++ better than they know the translations you happen to have picked. But I am a native English speaker, so I don't really know how much difference it makes. Given how many C++ programmers there are in the world who don't translate languages, I suspect it's not a huge deal compared with the fact that all the documentation is in English...

If every C++ developer was familiar with your aliases then why not, but you are with these aliases essentially introducing a new language to whoever needs to maintain your code.
Why add this extra mental step that for the most part does not add any clarity (&& and || are pretty obvious what they are doing for any C/C++ programmer, and any way in C++ you can use the and and or keywords)

Related

Are there technical limitations that prevent built-ins from having static members?

I find this atrocious:
std::numeric_limits<int>::max()
And really wish I could just write this:
int::max
Yes, there is INT_MAX and friends. But sometimes you are dealing with something like streamsize, which is a synonym for an unspecified built-in, so you don't know whether you should use INT_MAX or LONG_MAX or whatever. Is there a technical limitation that prevents something like int::max from being put into the language? Or is it just that nobody but me is interested in it?
Primitive types are not class types, so they don't have static members, that's it.
If you make them class types, you are changing the foundations of the language (although thinking about it it wouldn't be such a problem for compatibility reasons, more like some headaches for the standard guys to figure out exactly what members to add to them).
But more importantly, I think that nobody but you is interested in it :) ; personally I don't find numeric_limits so atrocious (actually, it's quite C++-ish - although many can argue that often what is C++-ish looks atrocious :P ).
All in all, I'd say that this is the usual "every feature starts with minus 100 points" point; the article talks about C#, but it's even more relevant for C++, that has already tons of language features and subtleties, a complex standard and many compiler vendors that can put their vetoes:
One way to do that is through the concept of “minus 100 points”. Every feature starts out in the hole by 100 points, which means that it has to have a significant net positive effect on the overall package for it to make it into the language. Some features are okay features for a language to have, they just aren't quite good enough to make it into the language.
Even if the proposal were carefully prepared by someone else, it would still take time for the standard committee to examine and discuss it, and it would probably be rejected because it would be a duplication of stuff that is already possible without problems.
There are actually multiple issues:
built-in types aren't classes in C++
classes can't be extended with new members in C++
assuming the implementation were required to supply certain "members": which? There are lots of other attributes you might want to find for type and using traits allows for them being added.
That said, if you feel you want shorter notation for this, just create it:
namespace traits {
template <typename T> constexpr T max() {
return std::numeric_limits<T>::max();
}
}
int m = traits::max<int>();
using namespace traits;
int n = max<int>();
Why don't you use std::numeric_limits<streamsize>::max()? As for why it's a function (max()) instead of a constant (max), I don't know. In my own app I made my own num_traits type that provides the maximum value as a static constant instead of a function, (and provides significantly more information than numeric_limits).
It would be nice if they had defined some constants and functions on "int" itself, the way C# has int.MaxValue, int.MaxValue and int.Parse(string), but that's just not what the C++ committee decided.

Why does C++ not allow user-defined operators?

I've been wondering this for quite some time. There are already a whole bunch of them and they can be overloaded, so why not do it to the end and allow custom operators? I think it could be a great addition.
I've been told that this would make the language too hard to compile. This makes me wonder, C++ cannot really be designed for easy compilation anyway, so is it really undoable? Of course, if you use an LR parser with a static table and a grammar such as
E → T + E | T
T → F * T | F
F → id | '(' E ')'
it wouldn't work. In Prolog, which is usually parsed with a Operator-Precedence parser AFAIK, new operators can easily be defined, but the language is much simpler. Now, the grammar could obviously be rewritten to accept identifiers in every place where an operator is hard-coded into the grammar.
What other solutions and parser schemes are there and what other things have influenced that design decision?
http://www2.research.att.com/~bs/bs_faq2.html#overload-operator
The possibility has been considered several times, but each time I/we decided that the likely problems outweighed the likely benefits.
It's not a language-technical problem. Even when I first considerd it in 1983, I knew how it could be implemented. However, my experience has been that when we go beyond the most trivial examples people seem to have subtlely different opinions of "the obvious" meaning of uses of an operator. A classical example is a**b**c. Assume that ** has been made to mean exponentiation. Now should a**b**c mean (a**b)**c or a**(b**c)? I thought the answer was obvious and my friends agreed - and then we found that we didn't agree on which resolution was the obvious one. My conjecture is that such problems would lead to subtle bugs.
It would become even harder to compile than what already is. Also, there would be problems with operators' precedence: how do you define it? You need a way to tell the compiler that an user-defined operator has precedence over another operator.
Almost surely it's feasible, but I think that C++ doesn't need other ways to shoot yourself in the foot :-)
This would make the language even more complex. And that obviously wouldn't be desirable.
Still, check out Boost Spirit. It goes a long way to make stuff like you mentioned possible using lots of template metaprogramming tricks.
Actually it's designed to be very easy to parse and compile. C has 32 defined keywords, all other tokens are function and variables.
C++ only has a few more. One can easily identify which token is for which, so know what to look for when one uses the + token or whatever.
The problem with allowing custom operators is that you also have to allow the programmer to specify the syntax for how the operators should be used. I suppose the C++ type system could help a little, but it would help resolving issues like associativity, etc.
It would make the already complex language, much more complex...
This is usually avoided because most code is written by more that one man, so the code should be "reviewable", and it's hardly a "desired" feature of a language.
Joel Spolsky have a good article about this.
I just found out that it's actually possible to achieve something very similar to overloaded operators. Consider
Vector v, a, b; v = a /vectorProduct/ b;
Turns out you can achieve the behaviour of custom operator by using dummy classes delimited by existing operators. =)

Is boost::variant rocket science? (And should I therefore avoid it for simple problems?)

OK, so I have this tiny little corner of my code where I'd like my function return either of (int, double, CString) to clean up the code a bit.
So I think: No problem to write a little union-like wrapper struct with three members etc. But wait! Haven't I read of boost::variant? Wouldn't this be exactly what I need? This would save me from messing around with a wrapper struct myself! (Note that I already have the boost library available in my project.)
So I fire up my browser, navigate to Chapter 28. Boost.Variant and lo and behold:
The variant class template is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types [...]
Great! Exactly what I need!
But then it goes on:
Boost.Variant vs. Boost.Any
Boost.Any makes little use of template metaprogramming techniques (avoiding potentially hard-to-read error messages and significant compile-time processor and memory demands).
[...]
Troubleshooting
"Internal heap limit reached" -- Microsoft Visual C++ -- The compiler option /ZmNNN can increase the memory allocation limit. The NNN is a scaling percentage (i.e., 100 denotes the default limit). (Try /Zm200.)
[...]
Uh oh. So using boost::variant may significantly increase compile-time and generate hard-to-read error messages. What if someone moves my use of boost::variant to a common header, will our project suddenly take lots longer to compile? Am I introducing an (unnecessarily) complex type?
Should I use boost::variant for my simple tiny problem?
Generally, use boost::variant if you do want a discriminated union (any is for unknown types -- think of it as some kind of equivalent to how void* is used in C).
Some advantages include exception handling, potential usage of less space than the sum of the type sizes, type discriminated "visiting". Basically, stuff you'd want to perform on the discriminated union.
However, for boost::variant to be efficient, at least one of the types used must be "easily" constructed (read the documentation for more details on what "easily" means).
Boost.variant is not that complex, IMHO. Yes, it is template based, but it doesn't use any really complex feature of C++. I've used quite a bit and no problem at all. I think in your case it would help better describing what your code is doing.
Another way of thinking is transforming what that function returns into a more semantically rich structure/class that allows interpreting which inner element is interesting, but that depends on your design.
This kind of boost element comes from functional programming, where you have variants around every corner.
It should be a way to have a type-safe approach to returning a kind of value that can be of many precise types. This means that is useful to solve your problem BUT you should consider if it's really what you need to do.
The added value compared to other approaches that tries to solve the same problem should be the type-safety (you won't be able to place whatever you want inside a variant without noticing, in opposition to a void*)
I don't use it because, to me, it's a symptom of bad design.
Either your method should return an object that implements a determinated interface or it should be split in more than one method. Design should be reviewed, anyway.

What are the bad habits of C programmers starting to write C++? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
A discussion recently ended laughing at the bad habits of programmers that have been too exposed to a language when they start programming in another language. The best example would be a Pascal programmer starting to #define begin { and #define end } when starting to write C.
Goal is to try to catch the bad habits of C programmers when they start using C++.
Tell about the big don't that you encountered. One suggestion by answer, please, to try to achieve a kind of best of.
For the ones interested in good habits, have a look at the accepted answer to this question.
Using raw pointers and resources instead of RAII objects.
using char* instead of std::string
using arrays instead of std::vector (or other containers)
not using other STL algorithms or libraries like boost where appropriate
abusing the preprocessor where constants, typedefs or templates would have been better
writing SESE-style (single-entry single exit) code
Declaring all the variables at the top of a function instead of as close as possible to where they are used.
Not using the STL, especially std::string,
and/or
using std::strings and reverting to old c string functions in tight corners.
Writing class definitions that are 2000 lines of code.
Copying and pasting that class definition into 12 different places.
Using switch statements when a simple virtual method would do.
Failing to allocate memory in constructor and deallocate in destructor.
Virtual methods that take optional arguments.
Writing while loops to manipulate char* strings.
Writing giant macro's that are a page in length. (Could have used templates instead).
Adding using's into header files so they can avoid names like std::string in type declarations.
using pointers instead of references
Very experienced developers not understanding casting or even object oriented programming:
I started helping out on a project and one of the senior guys was having a problem with some code that used to work and now didn't.
(Class names have been changed to protect the innocent, and I can't remember the exact names)
He had some C++ code that was listening to incoming message classes and reading them. The way it had worked in the past was that a Message class was passed in and he would interogate a variable on it to find out what type of message it was. He would then C-style cast the Message as another specialised class he'd written that inherited from Message. This new class had functions on it that extracted the data how he wanted it. Now, this had been working for him fine but now was not.
After many hours looking through his code he could not see a problem and I had a look over his shoulder. Immediately I told him that it's not a good idea to C-style cast Message to a derived class which it was not. He disagreed with me and said he'd been doing it for years and if that was wrong then everything he does is wrong because he frequently uses this approach. He was further backed up by a contractor who told me I was wrong. They both argued that this always works and the code hasn't changed so it's not this approach but something else that has broken his code.
I looked a bit further and found the difference. The latest version of the Message class had a virtual function and hadn't previously had any use of virtual. I told the pair of them that there was now a virtual table and functions were being looked up, etc, etc.... and this was causing their problem, etc, etc.... They eventually agreed and I was presented with a comment that I will never forget: "Virtual completely screws up polymorphism and object oriented programming".
I forwarded them a copy of a decorator pattern as an example of how to add a function to an existing class but heard nothing back from them. How they fixed the idea I have no idea.
One word: macros. I am not saying macros have no place at all in C++, but former C programmers tend to use them way too much after they switch to C++.
Using C-style casts.
C++ allows you to independently choose whether to allow casts between unrelated types, and whether to allow changes to const and volatile qualifiers, giving considerable improvements to compile-time type safety compared with C. It also offers completely safe casts at the cost of a runtime check.
C-style casts, unchecked conversions between just about any types, allow whole classes of error that could be easily identified by more restrictive casts. Their syntax also makes them very difficult to search for, if you want to audit buggy code for dubious conversions.
Assuming said programmers have already made the mistake of attempting to learn C++:
Mistakes
Not using STL.
Trying to wrap everything in classes.
Trying to use templates for everything.
Not using Boost. (I know Boost can be a real PITA, and a learning curve, but C++ is just C+ without it. Boost gives C++ some batteries).
Not using smart pointers.
Not using RAII.
Overusing exceptions.
Controversial
Moving to C++. Don't do it.
Try to convert C stdio to iostreams. Iostreams SUX. Don't use it. It's inherently broken. Look here.
Using the following parts of the libstdc++ library:
strings (beyond freeing them for me, go the hell away)
localization (what the hell does this have to do with c++, worse yet, it's awful)
input/output (64 bit file offsets? heard of them?)
Naively believing you can still debug on the command line. Don't use C++ extensively without a code crane (IDE).
Following C++ blogs. C++ blogs carp on about what essentially boils down to metadata and sugar. Beyond a good FAQ, and experience, I've yet to see a useful C++ blog. (Note that's a challenge: I'd love to read a good C++ blog.)
Writing using namespace std because everyone does and then never reflecting on its meaning.
Or knowing what it means but saying "std::cout << "Hello World" << std::endl; looks ugly".
Passing objects with pointers instead of references. Yes, there are still times when you need pointers in C++, but references are safer, so you should use them when you can.
Making everything in a class public. So, data members that should be private aren't.
Not fully understanding the semantics of pointers and references and when to use one or the other. Related to pointers is also the issue of not managing dynamic allocated memory correctly or failing at using "smarter" constructs for that(e.g. smart pointers).
My favourite is the C programmer who writes a single method with multiple, optional, arguments.
Basically, the function would do different things depending on the values and/or nullability of the arguments.
Not using templates when creating algorithms and data structures (example). It makes things either too localized or too generic
I.e. writing
void qsort(MyStruct *begin, size_t length); //too localized
void qsort(void *begin, size_t length,
size_t rec_size, int(compare*)(void*,void*)); //too generic
instead of
template <class RA_Iter>
void qsort(RA_Iter begin, size_t length);
//uses RA_Iter::value_type::operator< for comparison
Well, bad program design transcends languages ( casts, ignoring warnings, unnecessary precompiler magic, unnecessary bit-twiddling, not using the char classification macros ) , and The C language itself doesn't create too many "bad habits" ( Ok, Macros, esp from the stone ages ), and many of the idioms translate directly. But a few that could be considered:
Using a feature just because it's in C++ and so therefore it must be the right way to do something. Some programs just don't need Inheritance, MI, exceptions, RTTI, templates ( great as they are ... the debugging load is steep ), or Virtual class stuff.
Sticking with some code snippet from C, without thinking if C++ has a better way. ( There's a reason you now have class, private, public, const (expanded beyond C89) , static class funcs, references.
Not being familiar with the C++ i/o lib ( its BIG, and you do need to know it) , and mixing C++ i/o and C i/o.
He thinks that C++ is just a little more different language from C. He will continue programming C masked by C++. No advanced use of classes, the structs are considered less powerful than classes, namespace, new headers, templates, nothing of these new elements are used. He will continue declaring integer vars without int, he will not provide functions prototypes. He will use malloc and free, unsafe pointers and preprocessor to define inline functions. This is just a small list ;)
Confused uses of structs vs. classes, overuse of global methods that take object pointers as arguments, and globally-accessible instance pointers, a la:
extern Application* g_pApp;
void RunApplication(Application* app, int flags);
Also (not saying it's totally useless, but still):
const void* buf;
Declaring all the variables at the start of the function itself even if the variable will be used only after 100 lines or so.
Happens especially for local variables declared inside a function.
Solving the problem instead of creating a class-based monstrosity guaranteed to keep you in health insurance and 401K benefits.
Implementing lisp in a single file and doing the design in that.
Writing normal readable functions instead of overriding operators?
Writing in a style which can be understood by the junior programmers which see good practice as "not writing in C++".
Talking to the OS in it's own language.
Not leaving well enough alone, and using C instead.

Features of C++ that can't be implemented in C?

I have read that C++ is super-set of C and provide a real-time implementation by creating objects. Also C++ is closed to real world as it is enriched with Object Oriented concepts.
What all concepts are there in C++ that can not be implemented in C?
Some say that we can not over write methods in C then how can we have different flavors of printf()?
For example printf("sachin"); will print sachin and printf("%d, %s",count ,name); will print 1,sachin assuming count is an integer whose value is 1 and name is a character array initililsed with "sachin".
Some say data abstraction is achieved in C++, so what about structures?
Some responders here argues that most things that can be produced with C++ code can also be produced with C with enough ambition. This is true in some parts, but some things are inherently impossible to achieve unless you modify the C compiler to deviate from the standard.
Fakeable:
Inheritance (pointer to parent-struct in the child-struct)
Polymorphism (Faking vtable by using a group of function pointers)
Data encapsulation (opaque sub structures with an implementation not exposed in public interface)
Impossible:
Templates (which might as well be called preprocessor step 2)
Function/method overloading by arguments (some try to emulate this with ellipses, but never really comes close)
RAII (Constructors and destructors are automatically invoked in C++, so your stack resources are safely handled within their scope)
Complex cast operators (in C you can cast almost anything)
Exceptions
Worth checking out:
GLib (a C library) has a rather elaborate OO emulation
I posted a question once about what people miss the most when using C instead of C++.
Clarification on RAII:
This concept is usually misinterpreted when it comes to its most important aspect - implicit resource management, i.e. the concept of guaranteeing (usually on language level) that resources are handled properly. Some believes that achieving RAII can be done by leaving this responsibility to the programmer (e.g. explicit destructor calls at goto labels), which unfortunately doesn't come close to providing the safety principles of RAII as a design concept.
A quote from a wikipedia article which clarifies this aspect of RAII:
"Resources therefore need to be tied to the lifespan of suitable objects. They are acquired during initialization, when there is no chance of them being used before they are available, and released with the destruction of the same objects, which is guaranteed to take place even in case of errors."
How about RAII and templates.
It is less about what features can't be implemented, and more about what features are directly supported in the language, and therefore allow clear and succinct expression of the design.
Sure you can implement, simulate, fake, or emulate most C++ features in C, but the resulting code will likely be less readable, or maintainable. Language support for OOP features allows code based on an Object Oriented Design to be expressed far more easily than the same design in a non-OOP language. If C were your language of choice, then often OOD may not be the best design methodology to use - or at least extensive use of advanced OOD idioms may not be advisable.
Of course if you have no design, then you are likely to end up with a mess in any language! ;)
Well, if you aren't going to implement a C++ compiler using C, there are thousands of things you can do with C++, but not with C. To name just a few:
C++ has classes. Classes have constructors and destructors which call code automatically when the object is initialized or descrtucted (going out of scope or with delete keyword).
Classes define an hierarchy. You can extend a class. (Inheritance)
C++ supports polymorphism. This means that you can define virtual methods. The compiler will choose which method to call based on the type of the object.
C++ supports Run Time Information.
You can use exceptions with C++.
Although you can emulate most of the above in C, you need to rely on conventions and do the work manually, whereas the C++ compiler does the job for you.
There is only one printf() in the C standard library. Other varieties are implemented by changing the name, for instance sprintf(), fprintf() and so on.
Structures can't hide implementation, there is no private data in C. Of course you can hide data by not showing what e.g. pointers point to, as is done for FILE * by the standard library. So there is data abstraction, but not as a direct feature of the struct construct.
Also, you can't overload operators in C, so a + b always means that some kind of addition is taking place. In C++, depending on the type of the objects involved, anything could happen.
Note that this implies (subtly) that + in C actually is overridden; int + int is not the same code as float + int for instance. But you can't do that kind of override yourself, it's something for the compiler only.
You can implement C++ fully in C... The original C++ compiler from AT+T was infact a preprocessor called CFront which just translated C++ code into C and compiled that.
This approach is still used today by comeau computing who produce one of the most C++ standards compliant compilers there is, eg. It supports all of C++ features.
namespace
All the rest is "easily" faked :)
printf is using a variable length arguments list, not an overloaded version of the function
C structures do not have constructors and are unable to inherit from other structures they are simply a convenient way to address grouped variables
C is not an OO langaueage and has none of the features of an OO language
having said that your are able to imitate C++ functionality with C code but, with C++ the compiler will do all the work for you in compile time
What all concepts are there in C++
that can not be implemented in C?
This is somewhat of an odd question, because really any concept that can be expressed in C++ can be expressed in C. Even functionality similar to C++ templates can be implemented in C using various horrifying macro tricks and other crimes against humanity.
The real difference comes down to 2 things: what the compiler will agree to enforce, and what syntactic conveniences the language offers.
Regarding compiler enforcement, in C++ the compiler will not allow you to directly access private data members from outside of a class or friends of the class. In C, the compiler won't enforce this; you'll have to rely on API documentation to separate "private" data from "publicly accessible" data.
And regarding syntactic convenience, C++ offers all sorts of conveniences not found in C, such as operator overloading, references, automated object initialization and destruction (in the form of constructors/destructors), exceptions and automated stack-unwinding, built-in support for polymorphism, etc.
So basically, any concept expressed in C++ can be expressed in C; it's simply a matter of how far the compiler will go to help you express a certain concept and how much syntactic convenience the compiler offers. Since C++ is a newer language, it comes with a lot more bells and whistles than you would find in C, thus making the expression of certain concepts easier.
One feature that isn't really OOP-related is default arguments, which can be a real keystroke-saver when used correctly.
Function overloading
I suppose there are so many things namespaces, templates that could not be implemented in C.
There shouldn't be too much such things, because early C++ compilers did produce C source code from C++ source code. Basically you can do everything in Assembler - but don't WANT to do this.
Quoting Joel, I'd say a powerful "feature" of C++ is operator overloading. That for me means having a language that will drive you insane unless you maintain your own code. For example,
i = j * 5;
… in C you know, at least, that j is
being multiplied by five and the
results stored in i.
But if you see that same snippet of
code in C++, you don’t know anything.
Nothing. The only way to know what’s
really happening in C++ is to find out
what types i and j are, something
which might be declared somewhere
altogether else. That’s because j
might be of a type that has operator*
overloaded and it does something
terribly witty when you try to
multiply it. And i might be of a type
that has operator= overloaded, and the
types might not be compatible so an
automatic type coercion function might
end up being called. And the only way
to find out is not only to check the
type of the variables, but to find the
code that implements that type, and
God help you if there’s inheritance
somewhere, because now you have to
traipse all the way up the class
hierarchy all by yourself trying to
find where that code really is, and if
there’s polymorphism somewhere, you’re
really in trouble because it’s not
enough to know what type i and j are
declared, you have to know what type
they are right now, which might
involve inspecting an arbitrary amount
of code and you can never really be
sure if you’ve looked everywhere
thanks to the halting problem (phew!).
When you see i=j*5 in C++ you are
really on your own, bubby, and that,
in my mind, reduces the ability to
detect possible problems just by
looking at code.
But again, this is a feature. (I know I will be modded down, but at the time of writing only a handful of posts talked about downsides of operator overloading)