• Shadowman (unregistered) in reply to snoofle
    snoofle:
    As a former C++ coder, I tended to judiciously use operator overloading where it made sense. Unfortunately, I inherited an 800K+ LOC mess, where the authors believed that you should use every conceivable feature of C++, whether it made sense or not. After much debugging and banging of head on desk, I discovered the following in a deeply nested hierarchy:

    overload >> to write to database and return what was written overload << to read from database overload > to write to stream overload < to read from stream overload [] to read the nth record from a stream/result-set overload ++ to implicitly create a new version of the transaction in the db (version=version+1)

    The << was particularly annoying as innocent calls to cout would start hitting the db and loading the stored version of the object instead of the one you were trying to print - it drove me nuts trying to figure out why the variable I had just set seemed to still have the old value from the db - the debugger showed the correct value but cout showed the old value (from the db).

    The guy who wrote it had moved on to another team - I hunted him down and publicly ripped him a new one.

    For the record, to make it easier to understand what the thing was doing I replaced all the overloaded operations with descriptively named functions. At least new team members could follow what it was doing.

    One could argue that the standard library's overloading of the bitshift operators for insertion and extraction is already operator abuse...

  • Cameron (unregistered)

    Have you guys heard of MISRA C, IEC 61508, or the Nuclear Regulatory Commission NUREG-CR6463? The first is the car industry standard for C in embedded products, the second the standard for code in dangerous industrial systems. The third is the standard for software controlled nuclear power plants.

    These standards outright ban most language features including dynamically allocated memory. IEC 61508-7 C.2.6.2 suggests:

    no goto's no dynamicly created objects no dynamically created data no dynamic data structures no recursion no pointers

    In a business system, you can use nifty tricks but in a case where it has to work or lives are on the line, you don't want to be trying to figure out semantics that only a complier can interpret.

  • snoofle (unregistered) in reply to Shadowman
    Shadowman:
    snoofle:
    As a former C++ coder, I tended to judiciously use operator overloading where it made sense. Unfortunately, I inherited an 800K+ LOC mess, where the authors believed that you should use every conceivable feature of C++, whether it made sense or not. After much debugging and banging of head on desk, I discovered the following in a deeply nested hierarchy:

    overload >> to write to database and return what was written overload << to read from database overload > to write to stream overload < to read from stream overload [] to read the nth record from a stream/result-set overload ++ to implicitly create a new version of the transaction in the db (version=version+1)

    The << was particularly annoying as innocent calls to cout would start hitting the db and loading the stored version of the object instead of the one you were trying to print - it drove me nuts trying to figure out why the variable I had just set seemed to still have the old value from the db - the debugger showed the correct value but cout showed the old value (from the db).

    The guy who wrote it had moved on to another team - I hunted him down and publicly ripped him a new one.

    For the record, to make it easier to understand what the thing was doing I replaced all the overloaded operations with descriptively named functions. At least new team members could follow what it was doing.

    One could argue that the standard library's overloading of the bitshift operators for insertion and extraction is already operator abuse...

    Perhaps, but hardly at the level this guy descended to. One of my favorites was his concept of cumulative side affects, leading to such wonders as:

    // Struct-type determines which query to run
    dbStream << ObjectWithIdForQueryAndToStoreRetrievedRow
             << queryParam1
             << queryParam2
             << endl; // interpreted as: execute-query and 
                      // populate previously-specified-object
    
  • (cs) in reply to Zygo
    Zygo:
    brazzy:
    Zygo:
    Banning operator overloading is stupid. C++ is limited enough as it is, without intentionally ignoring its feeble attempts try to act like a real programming language.

    OK, I'll bite: what is, in your opinion, a "real programming language"?

    I still fail to see how syntactic sugar, added or subtracted, might possibly qualify or disqualify a language as limited or feeble.

    Zygo:
    Real programming languages can generate, inspect, or modify existing code at runtime, including (within reason) the code of the language compiler and runtime library.

    <snip>

    This is an assertion, not a definition. There is no particular reason why a "real" programming language should be able to do any of these things. I would suggest that your use of the qualifier "within reason" is a tacit acceptance of this.

    Personally, I see no reason to do any of these things at run-time (with the possible exception of "inspection," which does indeed help with frameworks, IDEs, and the like. If that's your thing). Such functionality comes with its own baggage, including (fundamentally) the inability to test it in any meaningful way.

    However, this is a perfectly decent set of principles for designing a language. Not an "unlimited" one; not an "unfeeble" one. Just not one that 99.999% of the commercial world would touch with a bargepole. In essence, you are arguing that the One True Language Grail is a pure functional language (presumably a lisp flavour such as haskell), and that's fine. But not really relevant.

    The code in the OP is still shit, in any language known to man.

    Zygo:
    real_aardvark:
    not to mention operator *->()

    Hmmm...that would permit smart pointer-to-member classes (analogous to smart pointer classes, but they choose a member function for you instead of an object). That opens up entire worlds of polymorphism...

    (Ooh, I wish you hadn't said that. I wasn't even thinking of the consequences of this particular overload. Now I feel sick ...)

  • AdT (unregistered) in reply to brazzy
    brazzy:
    Really, operator overloading is so rarely useful and so often harmful that banning it entirely is a sensible idea.

    Yeah, let's all use Java instead

    Bignum paula, bean, brillant, wtf;
    
    paula.multiply(bean.add(brillant)).equals(wtf);
    

    So much more readable than GMP's C++ interface.

    Let's ban STL (or even Boost) iterators, too. They use operator overloading (EEEEEVIL!) and they're so powerful, if the Java folks ever realize what they have to do without, they're going to file anti-discrimination suits to have the C++ coders stop using them.

    myname:
    Let me guess, you're in the group that thinks that Java not including unsigned variables is a good thing?

    C# is for people who want to have one arm strapped behind their back because it reduces the probability of making a mistake by 50%.

    Java is for people who prefer to have the arm chopped off.

  • Michael (unregistered) in reply to TexDex
    TexDex:
    When I was first learning Java and just beginning to understand the concept of OOP I hit on a great idea to demonstrate objects by making a "measurement" class. The way I planned it, you would be able to convert from one unit type to another, and multiply those units so they would follow logical rules. For example: If I added two Length objects I'd get another length object; if I multiplied two Length objects together it would return an Area object; if you divided a Length unit by a Time unit you would get a Speed object.

    So the code would look like this: maxHeight = myHeight + hatHeight cubeVol = h * w * d

    This would be a perfect and perfectly logical use of operator overloading, particularly useful for use in, say, physics simulations (not that I had any illusions about this being used in any real context but that was the general idea). I was pretty disappointed when I found that you couldn't actually make it look like that, and instead had to use awkward methods like .plus(x). I maintain that operator overloading, if used properly, adds to the readability of the code, and I've always been annoyed that Java didn't support it.

    Just out of curiousity, with this approach, how would you find the length of 5 football fields?

  • Michael (unregistered) in reply to myname
    myname:
    "Yeah, Java" + " doesn't do any " + "operator" + "overloading, " + "right?"
    In Java, this gets compiled into "Yeah, Java doesn't do any operator overloading, right?", just thought I'd point that out. Oh, and if you aren't using string literals, this approach is generally frowned upon, use StringBuilder instead.
  • AdT (unregistered) in reply to Zygo
    Zygo:
    C++ is limited enough as it is, without intentionally ignoring its feeble attempts try to act like a real programming language.

    You are limited enough as you are. I'll ignore your feeble attempts to try to act like a true scotsman.

    That's really one funny comment. I've heard many criticisms about C++ - complicated, hard to learn, unintuitive syntax (mostly uttered by people who want to have BEGIN and END back), etc., but limited? Thanks for the laugh - C++ is easily the most powerful programming language in terms of features. Now if you're talking about the standard library - which has little to do with the language - is so much more powerful as a containers and algorithms library than every alternative I'm aware of. The stinking abominations called java.util.* and System.Collections.* can kiss my ass. And when it comes to other tasks, there are many good libraries out there - Boost, wxWidgets, GMP, Blitz++ etc. etc. etc.

    Of course some will complain that there is no "standard" way of doing these things. What was the Java GUI library du jour right now? AWT? Swing? SWT? And in .NET - was it WinForms or WPF?

  • (cs) in reply to ali
    ali:
    p.p.s.: The real WTF is that the author thinks that NULL must be 0. Is not true.

    Please explain. Are you referring to the code author, or submission author (only the latter mentions NULL). In either case, even on systems where the machine representation of a null pointer is not bitwise zero, as far as the C++ language is concerned, (NULL==0). Am I wrong? (This must be a FAQ)

  • JedaFlain (unregistered) in reply to Zygo
    Once a problem domain reaches a critical level of complexity, the only solution available to a C++ developer is to implement a real language in C++, then implement the rest of the solution in the real language. Most people fail when they try to do this, they fall victim to Greenspun's Tenth Law, and their code ends up here.

    Doesn't the fact that you can code these "real languages" using C++ make C++ a real language by definition?

  • steve (unregistered) in reply to myname
    This example of operator overloading is a WTF, but there are plenty of instances where it can make code more readable and does make sense. String comparison and concatenation is one of the classic examples - very infrequently do you want to know if two strings use the same reference, and if you ever do, you can just cast them to (void*) to check anyway.

    No, if you have a pattern that you need to replicate in code, write a grammar for it and a separate tool to generate it at compile time. Don't force other people to learn your code idiosyncracies just because you found a clever language feature. I use C++ all day, and I have never found a case where operator overloading was required. If you mean "getNextThing", write "getNextThing". Its not like it takes any more CPU to execute that than an overloaded operator+ and its far clearer what your intentions are.

    Or have you never had to work on your own code 5 years later? Or worse, someone elses code 5 years after they've died?

  • AdT (unregistered) in reply to Zygo
    Zygo:
    Overloaded operators are rarely useful. 99% of the time, they're more trouble than they're worth, and they're not something anyone with less than two years' experience developing C++ with other human beings should attempt outside of a controlled academic setting.

    Boy, you're talking rubbish. Operator overloading is useful at least 90% of the time (iterators, math libraries, streams etc.), 5% of the time it's pointless but harmless and 5% of the time an idiot comes around (as in the article) and finds a fiendish thing to do with it. But who am I to contradict you - I only have 15 years of C++ experience, growing.

    Zygo:
    In LISP, you can redefine the meaning of an entire expression, not just the operators used within it.

    Now that's consistent - arguing against operator overloading because it can confuse people, and then saying that LISP allows you to overload virtually anything. But certainly in LISP, no one will be confused, ever!

    Zygo:
    Real programming languages can generate, inspect, or modify existing code at runtime, including (within reason) the code of the language compiler and runtime library.

    Is that the official definition from Merriam-Webster, or do I notice a faint trace of bowel aroma? I could just as well point out some things that are easy in C++ but hard in Common LISP and then proclaim that LISP is not a Real Programming Language(tm).

    Zygo:
    thanks to ISO C++ section 14.7.3, it is not possible to extend the set of available template specializations

    That's why you make the member template a lightweight wrapper to a function in a namespace scope class template. Same for function templates which cannot be partially specialized. Been there done that (although I still hope it's going to be simpler in C++0x).

  • (cs) in reply to real_aardvark
    real_aardvark:
    How the hell is a maintenance programmer supposed to guess what's going on here?

    This code is still bizarre. Why check that the next node is "false" (one has to hope that the end of the list is specified as NULL) and then go ahead and assign it to nptr anyway?

    Good point, he skips the first element in the list AND the last element in the list.

  • (cs) in reply to Michael
    Michael:
    Oh, and if you aren't using string literals, this approach is generally frowned upon, use StringBuilder instead.

    StringBuilder?

    Java != C#.

  • (cs) in reply to Zygo
    Zygo:
    Compare C++ with LISP, which has only a half-dozen or so operators (and that's really stretching the meaning of the word "operator"), no operator overloading, and a lot more expressive power than C++. In LISP, you can redefine the meaning of an entire expression, not just the operators used within it.

    A contrived Scheme example:

    (define square (lambda (x) (define + (lambda (y z) (* y z))) (+ x x)))

    (+ 4 4) => 8 (square 4) => 16 (+ 4 4) => 8

  • Zygo (unregistered) in reply to ali
    ali:
    p.p.s.: The real WTF is that the author thinks that NULL must be 0. Is not true.

    NULL must be a preprocessor macro that evaluates to an implementation-defined null pointer constant (C.2.2.3). The null pointer constant is an integral constant expression that evaluates to zero (4.10.1). Conversion from pointer type to a bool type returns false if the pointer type is equal to NULL and true otherwise (4.12.1), and conversion from integer to bool returns false if the integer is equal to zero and true otherwise (also 4.12.1). Comparison of pointers with the constant integer 0 is treated as comparison with the null pointer constant, which in turn is considered equal to NULL (5.10, 5.19, plus the standard conversions). Note that non-constant integer expressions and integer constants other than 0 are not allowed, so you can't write NULL == someNonConstInt or NULL == 1.

    So as far as the language is concerned, without using reinterpret_casts or void* conversions, NULL must for all practical purposes be 0: 0 can be converted to NULL, NULL can be converted to 0, the expression "((p == NULL) == (p == 0)) && ((p != NULL) == (p != 0))" must be true for all values of any pointer type, and so on.

    Speaking of reinterpret_casts and void* conversions:

        void *p1;
        memset(&p1, 0, sizeof(p1));
        void *p2 = NULL;
        void *p3 = 0;   // yes, this is allowed (4.10.1)
    
        if (p1 != p2) {
            std::cout << "NULL is NOT all-bits-zero on your platform." << std::endl;
        } else {
            std::cout << "NULL is all-bits-zero on your platform." << std::endl;
        }
    
        if (p2 != p3) {
            std::cout << "Your compiler violates the C++ standard." << std::endl;
        }
    

    On at least one platform the memory occupied by the value NULL is a special pattern in binary (e.g. 0x55555555). When you access a pointer within permitted C++ expressions with defined behavior, the C++ compiler can hide the fact that the bit pattern of the memory occupied by the pointer is not all zero. If you compare the pointer with NULL the compiler compares the binary value with 0x55555555, and if you cast the pointer to an integer the compiler must handle 0x55555555 as a special case, or add/subtract while casting to/from integer.

    So if you put a value into the pointer some way other than by using C++ assignment (e.g. by using memset to set all of the bits of the pointer to zero, bypassing the C++ pointer-lvalue-assignment-from-integer-rvalue mechanism), the resulting pointer value might not be the same as NULL.

    Generally this is only an issue on oddball hardware that has special support for a "invalid" address and that address is not 0, or where pointer is some non-trivial type that encodes access modes or data types in the address bits (e.g. segmented memory or early DEC Alpha chips). Like 1's complement arithmetic, it's allowed by the standard, but you'll probably never see it in real life.

  • Kiriai (unregistered)

    Operator overloading seems to be just syntactic sugar. You can just replace the operator with a function call of a different name.

    But operator overloading is needed for generic programming in C++. Operator overloading allows programmers to create types which mimic the semantics of built in types. This allows one to write a function that can work on both a built in type and a user defined type.

    You can see an example of the usefulness of this in the generic algorithms of the STL. For example, for_each works equally well on a built in array as it does on an Iterated container.

  • JL (unregistered) in reply to JedaFlain
    JedaFlain:
    Once a problem domain reaches a critical level of complexity, the only solution available to a C++ developer is to implement a real language in C++, then implement the rest of the solution in the real language. Most people fail when they try to do this, they fall victim to Greenspun's Tenth Law, and their code ends up here.
    Doesn't the fact that you can code these "real languages" using C++ make C++ a real language by definition?
    Probably not, since by that definition, *all* Turing-complete languages are "real languages". I presume the poster is trying to argue that there are other languages that are "more expressive" than C++, in that they allow concepts to be expressed more succinctly -- roughly the same way C++ is more expressive than assembly language.

    So he is probably trying to make a case for higher-level languages like Lisp or Haskell. But, as always, there is a trade-off: By using such languages, you may be able to develop more quickly and express concepts more clearly. On the other hand, relative to C++, you may also sacrifice performance, you have a smaller talent pool of developers, and you run the risk of coding something so clever you can't debug or maintain it... You don't have the language enforcing constraints that make a program easier to understand in a lower-level language. The same things are true (or used to be true) of C++ relative to other languages, like C.

    It's funny that we're seeing an argument here that C++ isn't powerful enough, when the article is arguing that C++ is too powerful for its own good.

  • (cs) in reply to Michael
    Michael:
    Just out of curiousity, with this approach, how would you find the length of 5 football fields?
    People in the know usually look up the length of a single football field and multiply it by 5.
    Michael:
    myname:
    "Yeah, Java" + " doesn't do any " + "operator" + "overloading, " + "right?"
    In Java, this gets compiled into "Yeah, Java doesn't do any operator overloading, right?", just thought I'd point that out. Oh, and if you aren't using string literals, this approach is generally frowned upon, use StringBuilder instead.
    Way to miss the point.
    Kiriai:
    Operator overloading seems to be just syntactic sugar. You can just replace the operator with a function call of a different name.
    Operator overloading, much like functions, variable names and iterators, is the kind of "syntactic sugar" that can make the difference between "nice and natural looking code" and "what's that humongous piece of crap".
    JL:
    the article is arguing that C++ is too powerful for its own good.
    Uh no, the article merely states (once again) that stupid people build very crappy things with powerful features. But that's not like it's fresh news, it's been the case since our ancestors knapped their first flint and domesticated their first flames.
  • (cs) in reply to steve
    steve:
    No, if you have a pattern that you need to replicate in code, write a grammar for it and a separate tool to generate it at compile time. Don't force other people to learn your code idiosyncracies just because you found a clever language feature. I use C++ all day, and I have *never* found a case where operator overloading was required. If you mean "getNextThing", write "getNextThing". Its not like it takes any more CPU to execute that than an overloaded operator+ and its far clearer what your intentions are.

    You seem to be arguing that simply because you've never found a use for operator overloading, no sensible use can exist. My work involves alot of vectors, matrices and quaternions, and I would argue that this:

    Vector position, velocity; float time;

    position+=velocity*time;

    Is much better than:

    position.add(velocity.floatMultiply(time));

    Operator overloading is fine so long as you follow two rules:

    1. The way the overlord functions must reflect how the operator works in reality (I'm looking at you, iostream)

    2. The overload should faithfully follow the known conventions for that operator. i.e. + should be a const method that takes one argument and returns a value of the same type as the class. (People are probably going to quote obscure pieces of the C standard at me for saying this)

    I'm not justifying the atrocity in the OP or people who think "database+=newRow;" is a good idea, but please, think about us poor souls doing maths all day.

  • (cs) in reply to Zygo
    Zygo:
    Real programming languages can generate, inspect, or modify existing code at runtime, including (within reason) the code of the language compiler and runtime library.

    The biggest clue that you have a real programming language is that you can 1) write a function body in the argument list of a function, and 2) generate a function body by combining data passed as arguments. C++ can do both of these things with templates, but template code generation is only at compile time (unless your C++ program has a compiler and debugger hidden in a library somewhere), and is highly restricted even then (thanks to ISO C++ section 14.7.3, it is not possible to extend the set of available template specializations available to the arguments of a member template after the member template has been declared or instantiated, no matter how damn useful that capability would be in practice).

    Another clue that you have a real programming language is that you can iterate over function bodies as you would any other data structures. C++ programs that can do this are called "debuggers" or "virtual machine emulators"--these capabilities are certainly not expected from standard C++ implementations. Real programming languages expect nothing less from their standard libraries.

    Another sign is that the language is very small--a complete syntax description for most real programming languages fits on a single page--and just about every language feature is implemented by library functions (although often library functions are themselves just code generators, so you get the same machine code at the end whether you used "x+y" or "add(x,y)").

    Templates and RTTI are about as far away from this kind of capability as Microsoft Word macros are from assembly-language programming.

    Once a problem domain reaches a critical level of complexity, the only solution available to a C++ developer is to implement a real language in C++, then implement the rest of the solution in the real language. Most people fail when they try to do this, they fall victim to Greenspun's Tenth Law, and their code ends up here.

    What a verbose way of saying "Lisp is the only real programming language"...

  • ali (unregistered) in reply to Zygo
    Zygo:
    On at least one platform the memory occupied by the value NULL is a special pattern in binary (e.g. 0x55555555). When you access a pointer within permitted C++ expressions with defined behavior, the C++ compiler can hide the fact that the bit pattern of the memory occupied by the pointer is not all zero. If you compare the pointer with NULL the compiler compares the binary value with 0x55555555, and if you cast the pointer to an integer the compiler must handle 0x55555555 as a special case, or add/subtract while casting to/from integer.

    Ouch, thanks for clarifying this. I just remembered that there is some oddity hidden in NULL. Nice to see that somebody knows how odd it actually is. :)

    Anybody knows how this is in early C, where you could leave out types meaning its a "processor word"? Like this:

    #include <stdio.h>
    
    void ouch( a, b )
    {
       if( a==0 )
          printf("%d\n",b);
       else
          printf("%s\n",b);
    }
    
    int main(void)
    {
       ouch(0,1);
       ouch(1,"wtf");
       return 0;
    }
    

    I think this is valid K&R C, and it's been abused like 15 quafizzillion times in the Obfuscated Code Contest. Looks pretty difficult to implement special treatment for NULL pointers if you don't even know whether a variable is a pointer.

  • (cs) in reply to AdT
    AdT:
    C# is for people who want to have one arm strapped behind their back because it reduces the probability of making a mistake by 50%.

    Java is for people who prefer to have the arm chopped off.

    And C++ is for people who think juggling chainsaws is an efficient way to get your hair cut, because nobody ever makes mistakes.

  • (cs) in reply to real_aardvark
    real_aardvark:
    Zygo:
    real_aardvark:
    not to mention operator *->()

    Hmmm...that would permit smart pointer-to-member classes (analogous to smart pointer classes, but they choose a member function for you instead of an object). That opens up entire worlds of polymorphism...

    (Ooh, I wish you hadn't said that. I wasn't even thinking of the consequences of this particular overload. Now I feel sick ...)

    Has nobody here ever used smart pointers? They rely on that overload to work properly.

    http://en.wikipedia.org/wiki/Smart_pointer

  • (cs) in reply to brazzy
    brazzy:
    And C++ is for people who think juggling chainsaws is an efficient way to get your hair cut, because nobody ever makes mistakes.

    haha :D

  • Andrew (unregistered) in reply to myname
    myname:
    Asd:
    I was going to say I hope Sun ignores the people asking for operator overloading in Java, but, without pointers, if it is restricted to the numeric operators it could be ok (no == overloading etc.).
    "Yeah, Java" + " doesn't do any " + "operator" + " overloading, " + "right?"

    It's sort of like Java's half-assed approach to multiple inheritance. There is no multiple inheritance in Java - except through interfaces.

    Let me guess, you're in the group that thinks that Java not including unsigned variables is a good thing?

    This example of operator overloading is a WTF, but there are plenty of instances where it can make code more readable and does make sense. String comparison and concatenation is one of the classic examples - very infrequently do you want to know if two strings use the same reference, and if you ever do, you can just cast them to (void*) to check anyway.

    Every language has built-in operator overloading. '+' is overloaded to add float and integer types.

    I like Java's interfaces more than multiple inheritance. One can define an interface for an API, like java.sql.* for JDBC/SQL, without first writing code.

    C++ has to choose between two parent classes with the same virtual method signature. Classes A & B have virtual void save(); methods; which one does their child class C inherit?

    Java's interfaces allow one to mix two unrelated packages with a HAS-A relationship. Class C extends class A. C has a member field, class B, to do the work defined in interface D. Just call the matching methods of B through interface D.

    Every animal cell on Earth has a Java-style interface. A cell has a mitochondrion member field to implement the eatSugar() method.

  • SomeCoder (unregistered) in reply to brazzy
    brazzy:
    AdT:
    C# is for people who want to have one arm strapped behind their back because it reduces the probability of making a mistake by 50%.

    Java is for people who prefer to have the arm chopped off.

    And C++ is for people who think juggling chainsaws is an efficient way to get your hair cut, because nobody ever makes mistakes.

    C++ can be used for that, certainly, but in actuality C++ is for those of us that need speed and efficiency and don't want to be held back by bloated frameworks and slow interpretors.

    And yes, some of us do still exist, contrary to what people like Jeff Atwood think.

    While I won't speak to Java (haven't used it for years), C# is like giving someone a plastic butter knife. Sure, you can probably cut through all those trees with it, but sometimes a chainsaw is really, really nice to have.

    And whoever said that you never need operator overloading certainly hasn't tried to use any of the STL... EVER.

  • (cs) in reply to real_aardvark
    real_aardvark:
    In essence, you are arguing that the One True Language Grail is a pure functional language (presumably a lisp flavour such as haskell)
    Haskell is not a Lisp flavour, and Lisp is not a pure functional language. Pure functional languages like Haskell are incapable of modifying code at runtime by definition - they cannot modify anything at runtime. That's what the "pure" means.

    For example, in Haskell you can't increment a variable by one, you have to define a new value that is equal to the old value plus one. (If it can prove that you will never use the old value again, the compiler can optimise this behind the scenes so it actually increments a variable, but you can never prove that this has happened.)

    Andrew:
    Every language has built-in operator overloading. '+' is overloaded to add float and integer types.
    Not every language, actually. For example, in OCaml '+' only operates on one integer type. Floats are normally added with the '+.' operator. You can redefine the '+' operator so that it will add floats, but then you can't use it for integers anymore.
  • nco (unregistered)

    the macro NULL does not exist in C++, you use 0

  • ali (unregistered)

    To those who dislike overloading: If you dislike reusing the symbol "+", why don't you dislike reusing the symbol "add"? Just because one consists of letters and the other one doesn't?

    In most C++ code, "+" gets reused for: (A) Sums of things, i.e., in the sense of an algebraic group, (B) Concatenation. This is obviously considered bad by several Java-programmers here.

    In Java (not even considering custom code), "add" gets reused for: (A) Sums of those things that cannot be +'ed, (B) all kinds of unrelated things (scroll down to find "add")

  • nco (unregistered)

    This reminds me a code I found in the company I work for, a very current code:

    they decided to implement a DateTime class in C++ ( falling in the pitfall of reinventing class like CompanyString or CompanyVector... just for fun I guess ).

    You may not know, but in C++ you can overload operator ++ as a preincrement operator or post increment operator ( ++a or a++ . pre-increment is generally used for objects because it does not make copy of the old value ).

    To differentiate between the two operators, one accept a signature with a dummy int parameter.

    Well in the company they thought this dummy parameter should be of some use, so the postfix increment operator became something like this:

    CompanyDateTime CompanyDateTime::operator++( int days ) { this->addDay( days ); return date; }

  • AdT (unregistered) in reply to brazzy
    brazzy:
    And C++ is for people who think juggling chainsaws is an efficient way to get your hair cut, because nobody ever makes mistakes.

    I've been using C++ for 15 years and my head is still on. I do make mistakes, but the problems this causes are typically fixable without much effort. I'd rather have "enough rope to shoot myself in the foot" at any time than having to ask daddy for permission whenever I need any potentially dangerous tool.

    Most pitfalls are ridiculously easy to avoid anyway. Most of the danger came from the C (near) subset in conjunction with lenient compilers. C++ still allows you to do everything you can do in C, but makes it harder to do it wrong by accident (sprintf vs. ostringstream is a nice example).

    Captcha: bathe (Hey, since when is that your business, Alex? You are in a safe place x-thousand miles away from my smell.)

  • Rich (unregistered) in reply to Someone You Know

    Java has had a java.lang.StringBuilder since 1.5, I know that's a bit cutting edge as it's only 3 years old and therefore not to be trusted. Anyway using (or comparing with) earlier versions of the language means you get to bitch about it so much more.

    Oh yeah "Yeah, Java" + " doesn't do any " + "operator" + "overloading, " + "right?" results in Yeah, Java doesn't do any operatoroverloading, right?, right

  • AdT (unregistered) in reply to Andrew
    Andrew:
    I like Java's interfaces more than multiple inheritance. One can define an interface for an API, like java.sql.* for JDBC/SQL, without first writing code.

    Oh great, that sounds just like the way I use abstract base classes in C++.

    Andrew:
    C++ has to choose between two parent classes with the same virtual method signature. Classes A & B have virtual void save(); methods; which one does their child class C inherit?

    Both, of course. The ambiguity is illusionary. When you use C as an A, A.save() is used. When you use C as a B, B.save() is used.

    Only when you want to say c.save(); or want to C to be a D and D declares an abstract save() method, the compiler will tell you that it is unable to figure out which method you mean - then you insert the code needed to resolve the ambiguity.

    Andrew:
    Java's interfaces allow one to mix two unrelated packages with a HAS-A relationship. Class C extends class A. C has a member field, class B, to do the work defined in interface D. Just call the matching methods of B through interface D.

    Do you want to suggest that composition does not work in C++? Then what have I been using all those years?

    Andrew:
    Every animal cell on Earth has a Java-style interface. A cell *has a* mitochondrion member field to implement the eatSugar() method.

    Well. Whatever. Actually, almost every eukaryotic cell has mitochondria.

    ali:
    Concatenation. This is obviously considered bad by several Java-programmers here.

    Using += on strings (unless you only have a few substrings) is bad in Java because Java does not have efficient string concatenation. So you have to use StringBuilder.

    In C++, += for strings is an efficient operation, so this criticism is moot here.

    • for strings can be inefficient in both languages, though, because a copy has to be created to hold the result.
  • Robert O'Connor (unregistered)

    ...

  • TexDex (unregistered) in reply to Michael
    Michael:
    TexDex:
    When I was first learning Java and just beginning to understand the concept of OOP I hit on a great idea to demonstrate objects by making a "measurement" class. The way I planned it, you would be able to convert from one unit type to another, and multiply those units so they would follow logical rules. For example: If I added two Length objects I'd get another length object; if I multiplied two Length objects together it would return an Area object; if you divided a Length unit by a Time unit you would get a Speed object.

    So the code would look like this: maxHeight = myHeight + hatHeight cubeVol = h * w * d

    This would be a perfect and perfectly logical use of operator overloading, particularly useful for use in, say, physics simulations (not that I had any illusions about this being used in any real context but that was the general idea). I was pretty disappointed when I found that you couldn't actually make it look like that, and instead had to use awkward methods like .plus(x). I maintain that operator overloading, if used properly, adds to the readability of the code, and I've always been annoyed that Java didn't support it.

    Just out of curiousity, with this approach, how would you find the length of 5 football fields?

    Length footballField = new Length(100,"yards"); //that was way back when Java didn't support enums

    footballField = footballField * 5; //in the case of integers, the * operator is overloaded to simply multiply the unit

    System.out.println(footballField.toUnit("yards")); System.out.println(footballField.toUnit("feet")); System.out.println(footballField.toUnit("furlongs")); System.out.println(footballField.toUnit("lightyears"));//for the curious

  • Anon (unregistered) in reply to Cameron
    Cameron:
    Have you guys heard of MISRA C, IEC 61508, or the Nuclear Regulatory Commission NUREG-CR6463? The first is the car industry standard for C in embedded products, the second the standard for code in dangerous industrial systems. The third is the standard for software controlled nuclear power plants.
    Never heard of them, and don't care.

    If you have to "ban" so many language features, all you've done is prove that C is entirely and completely the wrong language for the project. 20 years they may not have had a real alternative - but today, if C is the wrong language then just don't use it. Hell, write your own special-purpose language if it's that important and nothing else is good enough.

    If I worked somewhere that banned language features the instant they were misused, I'ld make it my mission in life to abuse and torture as many language features as possible in order to get the entire language banned.

    If "banning features" is considered more efficient than "training developers" then there's deeper problems - because if they can't teach the guy why that instance of operator overloading is a problem, what else are they incapable of teaching?

  • Gazzonyx (unregistered) in reply to myname

    [quote user="myname"][quote user="Asd"]... This example of operator overloading is a WTF, but there are plenty of instances where it can make code more readable and does make sense. String comparison and concatenation is one of the classic examples - very infrequently do you want to know if two strings use the same reference, and if you ever do, you can just cast them to (void*) to check anyway.[/quote]

    You can do that in pure C, but I'm pretty sure that C++ won't allow it (the use of void *, that is). Then again, most compilers will probably have a flag to enable it anyways. Not sure, to be honest.

  • (cs) in reply to SuperousOxide
    SuperousOxide:
    Well there is a getPrevNode(), for negative values.

    I'm not sure the poster's logic "Knowing that almost every pointer value plus 1 will result in a non-NULL pointer" makes sense. Since it's a pointer to a class, you don't automatically know WHAT the pointer value plus 1 will equal. The correct objection is that CFNode + integer = pointer, which is not the way arithmetic operation usually work.

    Well I think he ment that when CFNode is a NULL pointer the result will be non-NULL because you always add one. The only time it would result in a NULL pointer when CFNode's pointer is -1

  • jorgenplas (unregistered) in reply to snoofle

    Unfortunately our trade is littered with alpha-nerds who are obsessed with constantly proving how clever they are. Even if this means writing code that is difficult and costly to maintain and extend.

    Unfortunately I see the abuses of C++ all to often in C# generics and reflective code where it is used completely inappropriately.

    It is said that the carft of our trade is not in writing code that a compile can understand but in writing code that people can understand.

  • Andy (unregistered) in reply to Anon
    If you have to "ban" so many language features, all you've done is prove that C is entirely and completely the wrong language for the project. 20 years they may not have had a real alternative - but today, if C is the wrong language then just don't use it. Hell, write your own special-purpose language if it's that important and nothing else is good enough.

    There already is such a language: Ada. It fixes all of the WTFs that make C a bad choice for (security-related) embedded systems.

  • Zygo (unregistered) in reply to AdT
    AdT:
    Operator overloading is useful at least 90% of the time (iterators, math libraries, streams etc.),

    Anyone can use existing overloaded operators if they are designed and implemented by someone with a clue, and they are quite useful.

    Designing and implementing overloaded operators is another matter. Before you can even attempt to use overload operators in a new design, you need to understand well how overloaded functions and all the type conversions work in C++, especially the implicit ones (although once you do understand those, operator overloading is a trivial special case).

    The filter I give to junior developers is: if there's no blindingly obvious choice for an overloaded operator, don't use one. If in doubt what "blindingly obvious" means, ask three people what a typical expression involving your operator does, and if there's more than one answer (including your own), don't use an operator.

    OTOH, if there is a blindingly obvious operator to use, then use it...but don't expect that to happen every day.

    AdT:
    Now that's consistent - arguing against operator overloading because it can confuse people, and then saying that LISP allows you to overload virtually anything. But certainly in LISP, no one will be confused, ever!

    I was arguing against indiscriminant operator overloading, especially by inexperienced developers, who think that it's useful in lots of places where it actually isn't. The problems arise when people start using overloaded operators (or other well-understood function names) for purposes that are significantly different than expected.

    In other words, overloading operator+() is the same as overloading strcmp()--both have their uses (e.g. "matrix + matrix" or strcmp(std::string, std::string) and abuses (e.g. "matrix + matrix = integer" or "strcmp(std::string, str

    Operators are effectively an implied, incomplete, but nonetheless well understood interface. Everyone knows what roughly "+" does with two numeric types in non-boundary cases. Nobody knows what "+" does with two numeric types in boundary cases (overflow? saturate? random junk? throw an exception? transparently promote to a larger type? rounding error?), or with types where more than one reasonable interpretation of the name "+" exists (catenate? add? make positive? do nothing?) or with types that are not obviously numeric (string + picture + salad = ?). It's only experience that tells you whether overloading "+" is horribly ambiguous and deceptive, or exactly the right thing to do, and it's safer by far if you're inexperienced to prefer the latter.

    Contrast with LISP, where most of the equivalents of the C++ operators are clearly library functions. There is no difference in LISP between an operator or any other kind of function, even at a superficial syntax level, yet no LISP coders (well, none that I've asked) complain about operator overloading (well, at least not more than they complain about any other aspect of life with LISP ;-).

    Technically C++ operators are (mostly) just overloaded functions, but a lot of people who work with C++ get irrational about the operators. It's all a matter of perspective. I wouldn't be surprised if half the people who complain use overloaded operators provided by system libraries in their code every day, and weren't even aware it.

    AdT:
    Zygo:
    thanks to ISO C++ section 14.7.3, it is not possible to extend the set of available template specializations

    That's why you make the member template a lightweight wrapper to a function in a namespace scope class template. Same for function templates which cannot be partially specialized. Been there done that (although I still hope it's going to be simpler in C++0x).

    Ummm, you're right of course.

    The even more interesting thing is that I went to find my example of this problem and discovered that apparently I already found the solution, but didn't use it. I wonder why. It must have not worked on Microsoft's compiler or something...

  • afeaewf (unregistered) in reply to AdT
    AdT:
    brazzy:
    And C++ is for people who think juggling chainsaws is an efficient way to get your hair cut, because nobody ever makes mistakes.

    I've been using C++ for 15 years and my head is still on. I do make mistakes, but the problems this causes are typically fixable without much effort. I'd rather have "enough rope to shoot myself in the foot" at any time than having to ask daddy for permission whenever I need any potentially dangerous tool.

    Most pitfalls are ridiculously easy to avoid anyway. Most of the danger came from the C (near) subset in conjunction with lenient compilers. C++ still allows you to do everything you can do in C, but makes it harder to do it wrong by accident (sprintf vs. ostringstream is a nice example).

    Captcha: bathe (Hey, since when is that your business, Alex? You are in a safe place x-thousand miles away from my smell.)

    Well, you know, it's great that you claim you're so fantastic with C++, but really, C++ abuse/misuse happens a lot more than you think it does.

    I'm not sure why you keep stating the "I've been using C++ for 15 years". I've been programming for 10 years now, and well, other than the time involved, I can't see how it makes me any more qualified to defend or criticize a programming language than, well, anybody else, even those novice programmers that can't tell apart a float from an int. Certainly, defending any programming language isn't really my idea of automatic expertise of said language.

    Quite frankly, I've never met a programming language I didn't hate after a few months. Familiarity breeds contempt.

  • AdT (unregistered) in reply to Cameron
    Cameron:
    Have you guys heard of MISRA C, IEC 61508, or the Nuclear Regulatory Commission NUREG-CR6463? The first is the car industry standard for C in embedded products, the second the standard for code in dangerous industrial systems. The third is the standard for software controlled nuclear power plants.

    These standards outright ban most language features including dynamically allocated memory. IEC 61508-7 C.2.6.2 suggests:

    no goto's no dynamicly created objects no dynamically created data no dynamic data structures no recursion no pointers

    I have heard of MISRA C, at least (I work in this business although I don't write embedded software myself). IIRC MISRA C does not forbid the use of recursion and pointers, instead, the non-usage of pointers is a required rule.

    "Huh?" people may ask, "Isn't that the same?". To clarify: In MISRA C, there are required rules and advisory rules. Whether you act upon advisory rules is your business. For required rules, you have to have a "process", however, to be compliant with MISRA C. That means, you can break a required rule, but you must provide a reason for doing so, and this reason must be documented. Thus, for example, you can use pointers if there is no other solution, or all other solutions are worse, but this needs to be justified and recorded so that in the event that someone wants to know whether, where and why you use pointers, you can immediately show the corresponding document and explain.

    There is, by the way, little use for dynamic memory allocation in embedded systems like that. An important operating system for automotive systems is OSEK, and while OSEK provides multi-tasking, it does not provide means to create or destroy tasks at run-time as e.g. Windows and all shades of *nix do. Instead, all the tasks you are going to need are specified at compile-time, and then the relevant parts of the OS are recompiled to include only those tasks (e.g. a static array with task information, just large enough for the tasks you configured). While this approach would be way too inflexible for a PC, it greatly reduces the amount of code that's necessary for task management at run-time (basically only state management and scheduling remains) and allows OSEK to be lean and mean enough to run on hardware with very little RAM and ROM (which is quite often still measured in kilobytes).

    Also, malloc is usually not available on embedded devices of this class - if you need a buffer, you create a global variable of sufficient size. Again, the benefit is that the OS can do without memory allocation and deallocation routines which would consume precious ROM space. Other problems avoided in this way are:

    1. malloc can fail at run-time, but declaring a statically allocated buffer that is too large results in a link-time failure, i.e. this sort of problem can be detected before you even load the ROM onto the test hardware (of course you need to test the code before it goes into production).
    2. malloc can require an only semi-predictable amount of time to complete its work - a problem when real-time behavior is required. Allocating static buffers is O(1).
    3. malloc can fail even if sufficient space is available, but the heap is too fragmented. .NET solves this problem by using two heaps and a copying garbage collector. But this means your heap has to be 2x the size actually used. This is ok on a PC where the GC can allocate a second heap from the OS to perform its work and free the first (now empty) heap afterwards (this is a shameless simplification, in the real world the .NET garbage collector has to deal with nasty things such as pinning references). Unless all processes on the system would try to do this at the same time, it is unlikely that this leads to a shortness of RAM. In the embedded system, this is not possible. Heap compaction would also be possible, but this requires all tasks to enter a safe state and be halted for the time of compaction, this is, again, bad for real-time behavior.
  • (cs) in reply to eh

    Does overloading << and >> for completely new and exciting things such as streams seam like a good idea to you? Or what about the boost::spirit parser that almost implements BNF syntax in C++?

    As I see it, these are all horrific attempts at making C++ a bit "nicer" by abusing language features. For every sane use of operator overloading I see, I see an order of magnitude more abuses.

    Sure, build overloading of the very common things such as integers, floats, matrices and complex numbers into the language, but don't let people define their own overloads.

    The problem with C++ (and guns) isn't that you can shoot yourself in the foot. It is that you can shoot me!

  • Zygo (unregistered) in reply to Cameron
    Cameron:
    Have you guys heard of MISRA C, IEC 61508, or the Nuclear Regulatory Commission NUREG-CR6463? The first is the car industry standard for C in embedded products, the second the standard for code in dangerous industrial systems. The third is the standard for software controlled nuclear power plants.

    These standards outright ban most language features including dynamically allocated memory.

    Dynamically allocated memory is evil for real-time systems. You need to know how long a given operation is going to take, and unless you're dealing with utterly trivial data structures, the runtime calculation usually a big complicated function that changes every time someone opens the source in a text editor.

    Of course, if you have an utterly trivial data structure, you can probably allocate it statically.

    Cameron:
    IEC 61508-7 C.2.6.2 suggests:

    no goto's no dynamicly created objects no dynamically created data no dynamic data structures no recursion no pointers

    No gotos is interesting, but not that surprising when you consider cases like jumping into or out of blocks (especially in C with half-assed embedded systems compilers, which often get these cases outright wrong).

    Recursion is usually evil for mission-critical systems because of the really horrible things that happen if you run out of stack space, especially on embedded CPU's that might not provide any "supervisor" mode to go into when a stack fault is detected...or for that matter, might not detect stack faults.

    No pointers is interesting, considering how few uses for pointers there are after banning all the other stuff, and how much performance can be lost on an embedded CPU without using them for basic array iteration. You can get a pointer easily enough just by abusing array indexes, or do they require array bounds checking in the implementation too? (No, that doesn't make sense, one bug would detonate the power plant)

  • AdT (unregistered) in reply to afeaewf
    afeaewf:
    Well, you know, it's great that you claim you're so fantastic with C++, but really, C++ abuse/misuse happens a lot more than you think it does.

    Where did I claim to be "so fantastic with C++"? I know the language quite well, however, and I have read most of the important books on C++ (Stroustrup's, Sutter's, Meyer's, Alexandrescu's and others). You can shoot yourself in the foot, but if you follow the advice contained in these books, if you make thoughtful use of design patterns, try to interface with the STL where possible etc., then the risk is greatly reduced. It just requires some discipline where the Java compiler would use coercion instead. (With the disadvantage of also often preventing you from acceptable usage of certain language features.)

    afeaewf:
    I'm not sure why you keep stating the "I've been using C++ for 15 years". I've been programming for 10 years now, and well, other than the time involved, I can't see how it makes me any more qualified to defend or criticize a programming language than, well, anybody else, even those novice programmers that can't tell apart a float from an int.

    Then you don't seem to have learned a lot about the programming languages you have been using. I feel justified in defending C++ against some criticism that I know from experience to be of little or no relevance to practical usage, or at least the way I use the language. In other words, the problems can be avoided. For instance, if someone says "C++ is insecure because of sprintf" (yes, I heard that claim), I feel qualified to inform them that sprintf is deprecated and it is much better (and more common) to use iostream, which makes it way harder to generate buffer overflows or pointer fuck-ups than sprintf.

    afeaewf:
    Certainly, defending any programming language isn't really my idea of automatic expertise of said language.

    Oh, where did I say that defending C++ makes me a C++ expert? If anything, it's the other way round - if I am a C++ expert, this qualifies me for defending the language against unfair or inaccurate criticism. Sure there is accurate criticism as well, but really a lot of what I hear about C++ is not.

    afeaewf:
    Quite frankly, I've never met a programming language I didn't hate after a few months.

    Then maybe you should consider switching professions. I accepted the fact that there is no One True Programming Language that was born of a virgin and is free of blame a long time ago. For example, I'll readily admit that some of the template syntax in ISO C++ is needlessly confusing, the basic_string class template has a too fat interface, the iterator concepts could be improved (see Boost). But on balance C++ is fine for me and much more useful than most othr languages. However, I also like to use Perl for some tasks where C++ is too "heavyweight", and Haskell as a "better calculator".

  • Jon (unregistered)

    I used to think that C++ was a great, powerful language. Then I used some languages that weren't C++ and discovered all the stuff I was missing out on.

  • Zygo (unregistered) in reply to AdT
    AdT:
    Zygo:
    C++ is limited enough as it is, without intentionally ignoring its feeble attempts try to act like a real programming language.

    That's really one funny comment. I've heard many criticisms about C++ - complicated, hard to learn, unintuitive syntax (mostly uttered by people who want to have BEGIN and END back), etc., but limited? Thanks for the laugh - C++ is easily the most powerful programming language in terms of features.

    C++ is little more than a macro-driven assembler designed by a committee. Admittedly it has a Turing-complete macro language, but C++ is still a macro-driven assembler that forces developers to cope with--or at least be careful to avoid--register-level execution details while they're trying to build an e-commerce site. C++ is probably one of the most powerful macro-driven assemblers commercially available, but that's all it is.

    AdT:
    Now if you're talking about the standard library - which has little to do with the language - is so much more powerful as a containers and algorithms library than every alternative I'm aware of. The stinking abominations called java.util.* and System.Collections.* can kiss my ass. And when it comes to other tasks, there are many good libraries out there - Boost, wxWidgets, GMP, Blitz++ etc. etc. etc.

    About the only thing C++ has going for it is widespread implementation and compatibility with C and assembly language. The STL is nice, but the STL existed for some years before many C++ compilers were able to compile it. Otherwise, C++ is a bit of a bastard child--real systems work gets done in C, real application development gets done in non-C++ languages (or it gets done in C++ at 10 times the development cost or bug count on delivery).

  • (cs)

    This must be what they call the BOOFH...

Leave a comment on “Yet Another Operator Overloading Abuse”

Log In or post as a guest

Replying to comment #:

« Return to Article