The hidden cost of C++

I mentioned it on another thread. It would be great to have a Concatable<T> interface. Let List<T> implement Concatable<List<T>>, for example. ! or [] probably would be good, too. Arithmetic operators are a little more troublesome.


Complex x, y, z;
// Assign initial values
z = x + y;
z.real += 1; // Okay

z = x;
x.real += 1; // Hmm

z = x + 0 + 0 + 1 - 1 + 2 - 2 + 0 * y; // What does it do? What methods get called? How many Objects need to / should get created?

There’s no confusion that string + 5 is a different type of operation than number + 5. (Given you have the variable declaration or you can infer it based on whether you see function calls or -, *, /, or % operators operating on the variable nearby.) There is also no confusion that one returns a reference to a new object and one uses by value assignment semantics. Without adding a new type of type, then retrofitting classes to support arithmetic operator overloading is less effective.

I understood pretty much immediately that it was an equation implemented in a sequence of operations.

In my experience working with C++, enhancing maintainability or increasing readabilty was not what operator overloading does. Actually, that was pretty much of the point of the original article - you look at the code and think it does X where it actually does Y because in this case the meaning of the operators has all changed.

Obviously you have different intuitions about readability, that’s cool. I have a lot of experience of using C++ that shows operator overloading being a maintenance nightmare. That doesn’t mean that a language couldn’t do it better, I suppose.

If you only look at languages like C++ and Perl to decide if a given language feature is good or not…you’ll quickly decide that assem is king. Forget shit designed languages…esp if they are semi-popular. They might be practical/pragmatic to use at some point in time, but that’s it.

And code readability, specially in a development team, has a massive impact on time.

In my opinion, it is always a balancing act between performance and development goals (aka deadlines). And the positive side of structured readable code is that you can always optimize bottlenecks afterwards.

The again, if you go crazy with overloading things can get worse very quickly… So… Code responsibly?

[quote=""]
???

C++ is a dead end in the evolution of programming.

Cas :slight_smile:

So are Dinosaurs, and they are still cool.

My problem with operator overloading is operator behavior. There are 2 main aspects to that. Mathematical behavior and well “instance” behavior.

Mathematical is perhaps the biggest. If i have a field with + and * that is also commutative then well we are fine. However this is a pretty small set of types where this makes sense. Even matrices don’t fit that, and so you are forced to add more meaning onto overloaded operators than the originals have. (ie can’t reorder the same way)

The next is also problematic. a+b means we need to copy a then add b to a. Otherwise everything gets screwed for larger statements. say (a+b)b+ac for example. This is what happens in C++ IIRC, and can end up really slow with the large amounts of bit wise copies that the compiler can’t eliminate. In java the copy would need to be well defined (deep enough) to not interfere with operator overloading.

Having written things for GF(2^m) and Z_p in java, where we do fit the math behaviors of +*. Operator overloading would have been useless for creating anything that had decent performance.

Devil’s advocate mode: We can agree that floats are not R and 2’s comp integers are not Z. And that many properties/identities of R and Z do not hold for their computer equivalent. There is no special operators to indicate, for instance, that add/sub/mul in floats are not associative nor integers wrapping/modulo properties. I have no problem with that. Likewise for non-commutative products…don’t have a problem with it being represented by the same token as one that commutes over some other type. All of this boils down to if you don’t understand the type, then you really can’t understand the operations anyway.

WRT: compilers and transforms…I’m too lazy to comment ATM. Esp since we all know this is just blah, blah, blah.

Things in IEEE whatever are commutative. ab=ba and a+b=b+a. Round of error etc is required to be exactly the same. Z_2^m is also exact. so no idea what you are talking about.

I like that +, -, * and / are available in GLSL, but that’s only because some GPUs are vector processors so they’re actually faster. Matrix-vertex multiplications are also extremely optimized…

We just started C# at school, and I must admit that many of the tricks my teacher is telling us, I’d rather be without, but some are just to die for.


public string Name{get; set;}

:smiley:

Also, I like that I can overload my list-operator, so I can control how it adds lists together. You know, so I can make it add things from another list so they’re sorted, according to some interface or what not.

Also, this rocks:


// Method:
public void SetNameAndAddress(string name, string address){ Name = name; Address = address; }

// Usage:
p.SetNameAndAddress(address: "Vestergade", name: "Jette");


// Method with default values:
public void method(string name = "Lise", string address = "Skolevej 2", bool female = true)
        {
            Name = name;
            Address = address;
            Female = female;
        }

// Usage:
p.method(Address: "Borderlands 42");

But I also constantly think…“man, these many smart things, are going to make things very confusing.”
Delegates are cool, but I think I’d have trouble understanding what’s going on while reading through someone’s code plastered with them.

Named parameters - I like.
Default parameter values - I like.

Java Y U NO HAV THESE THINGS?

Cas :slight_smile:

Well, I don’t base my opinion on just C++. I’ve had a look at how Scala does operator overloading, and it’s better than C++, but it still has the same flaws:

(1) Can’t tell at a glance what a = b + c means without knowing the types of a,b,c and reading their definitions if they’re classes
(2) Operators inherit precendence from primitives even if it’s no longer appropriate (e.g.: Vectors: a * b (cross product) should have the same precedence as a + b (addition) but the cross product gets higher prescendence because it inherits the precedence of multiplication
(3) You can’t all the operators you want. E.g. Vector dot product: I can’t have a . b for vectors.

Issue 1 causes code to be hard to maintain, issue 2 causes the code you write to NOT behave like the actual written out maths because the precedence has to obey that of a different domain and issue 3 causes the whole implementation to be half arsed in the first place. I don’t hate the idea of operator overloading in principle, but in practice it’s always compromised. If you can’t even actually represent the maths correctly due to problems 2 & 3, why even bother paying the cost of issue 1 to get a compromised half measure?

Yup - I’d definitely agree with these. I also really like Javascript’s object and array literals. And JSON. And lambdas.

I would never use * for cross product. In glsl it’s component wise multipliaction for vectors so that would confuse many ppl.

QED :slight_smile: And thus the thread neatly explained itself.

Cas :slight_smile:

@delt0r: my non-commutative comments only about non-primitive types. Float ops are worse in that they are non-associative and not (normally) special cased, so I don’t find any big deal about using the common product token for non-commutative products.

@kaffiene: To me it seems like most people seem to base their opinion on C++ operators and a very high percentage without any real first hand experience. I’ve no knowledge of scala so I cannot really comment on that implementation. I look more to mathematical DSLs than any general purpose language for reasonable operator overloading.

Not having operators doesn’t change this fact. With or without the situation is identical.

I’ve always seen inner and outer products having higher president than add/sub. I cannot see any logical argument to give them the same. But ultimately that’s a different story because the outer product of vector analysis isn’t a product so why overload the product operator in the first place? And besides some language do indeed allow user defined prescience rules…but that’s a different can of worms and something that the vast majority of people would totally hate. Personally I’m not in favor of.

No argument…more operators is all good. If fact many algebras need many operators for operator overloading to be of any real usage. Example Clifford algebra pretty much needs product, geometric inner and outer products, Euclidean inner and outer products and multiple conjugates to be generally useful. But really this is a different issue.

Nor should you as it’s not a product. It’s an outer product, specifically: (ab-ba)/2…as such overloading product is an illogical choice. Only being about to choice from basic operators, then the only logical choice is “^” as the wedge product is a semi-standard notation for outer products. (SEE: later)

Exactingly…we agree from different perspectives. You can’t prevent programmers from writing bad code, so don’t even attempt to do so.

WRT: vector analysis. I don’t care about operator overloading for this algebra. Why? Vector analysis is like C++. It’s totally awful, which is not to be confused with useless. It’s in fact very useful, but it’s a horrible algebra. It has virtually no operators and operator chains tend to be very short…so explicit method calls instead of operators is no big deal.

I don’t understand why this kind of thread is going on AGAIN…but I’ll just repost sproingie’s brilliant post:

I suspect this was the only post in this topic made after having read “The hidden cost of C++” and made in reaction to that topic. The intent behind the thread presents an entirely different point of view than what was mentioned in the other thread, but it instantly got derailed into a “I want feature X from other language Y” thread.

There’s a huge problem that is yet to be addressed here. In C++, every variable is its own object. a = b; (always) creates a copy of b and (always) overwrites and destroys the original value of a. This is why people advise each other not to sort an STL vector of strings. Quicksorting the list of Strings makes O(n log n) assignments, meaning it makes O(n log n) assignments, meaning it has to allocate new memory for a string O(n log n) times and copy the array of characters O(n log n) times. In order to not suffer poor performance due to slow heap memory allocations (ironic), some people advise each other to use a selection sort on string vectors even though it is otherwise less efficient because it makes only O(n) assignments. (Now, I don’t know how much of a performance hit it causes now or if professional C++ programmers know better, but… you should note how foreign C++ is to other languages. Especially Java.)

Using this approach for objects has a lot of disadvantages (readability, maintainability, coding time, and lost opportunity for certain compiler optimizations), but it works well for user created numerical types that are a fixed size and don’t involve pointers. It means that the compiler can allocate space for a variable on the stack or even in registers for custom number types. It can create the bare minimum space required and reuse space to hold intermediate values while evaluating an expression. It’s still problematic, but because every variable is its own object you avoid having to make design choices as you would to implement operator overloading in Java.

In Java variables and objects are separate notions. Since Objects are currently the only custom type you can create in Java, you have to retrofit operator overloading to work with capital O Objects. How do you force immutability? Where do you allocate space? Does = make a copy as expected of primitives or does it alias two variables to the same Object as expected of class references? Would you need to create a new garbage collector to accommodate extra objects created by using operator overloading? What do you do if a reference gets leaked to another class in the middle of evaluating an expression? Do you need to create a new instance for every intermediate value? You see, operator overloading in Java would not only have the same hidden costs as C++, but if you did not change the language to create a new type of custom type then it has even more hidden costs.

C’mon, no minds will be changed, true, but the discussion is very interesting and informative.