Very well stated. Kudos
Printable View
I tend to agree with the notion of reference semantics as meaning both pointers and C++ references. After all, they both refer to something else, and that is an essential part of their meaning. Take for example part of Stroustrup's answer to the FAQ Why are the standard containers so slow?:Quote:
Originally Posted by PredicateNormative
Post-C++11, Stroustrup's comment can now be replaced by a comment on move semantics, but the fact remains: Stroustrup regarded "handles" (which can essentially mean smart pointers) and "pointers" as having "reference semantics".Quote:
Originally Posted by Bjarne Stroustrup
That said, it would have been better to avoid talking past each other by defining the terms.
unfortunately, I don't think so. The original issue was not just about the dangers of (raw) pointers, but rather their role in interface designs.
I ( and probably OReubens ) claimed that values and then references should be preferred over pointers ( dumb and ( to a less extent ) smart ) whenever possible/reasonable.
Whereas, tilavirga ( as far as I can tell ) claims that smart pointers should be *the* preferred ( and possibly only ) way of dealing with polymorphic objects; IOW, c++11 smart pointers ( referring to some object ) are to c++ as variables ( referring to some object ) are to Java.
As a consequence of this claim it follows that the primary way of dealing with polymorphic objects ( always according to tilavirga's reasoning ) is via reference semantics.
Now, both tilavirga and TheGreatCthulhu claim that value and reference semantics are equally safe at a design level, and that any observed issue about the latter ( in c++ ) should be imputed to a developer ignorance concerning OOP and/or to the use of unsafe constructs like raw pointers and such.
Conversely, I claimed that reference semantics is intrinsically more complex than value semantics ( given two otherwise identical interfaces ) and that if we want to pass an object of type T,
---> responsabilities of the "handle" --->
T, T const&, T&, unique_ptr<T>, weak_ptr<T>, shared_ptr<T>, T const*, T*
we should always choose the type with the the smallest responsability compatible with the interface semantics.
Correct. I do maintenance/revision a lot and you often see devs essently doing little more than writing 'raw pointer' interfaces and then just wrapping the pointers in a smart object and then patting themselves on the back for a job well done and a 'clean design'. With that in mind "pointers are evil" are a shorthand for getting 'bad developers' to think harder when they do introduce pointers. And as I stated before, sometimes you have to go with evil anyway because there's no viable alternative.
And the same is true for just about every other "X is evil"
One Obvious one is 'goto is evil'. There are good uses of goto too. It's funny to see people that aggressively oppose any and all uses of goto, but at the same time advocate continue/break to get out of loops. continue/break is just a 'mini goto' so it falls into the same 'valid uses' cases. using goto to 'break' out a nested loop... I see no problem with that at all.
using goto to just jump anywhere... yes, I'll agree that's a definate nono.
While smart pointers are better than raw pointers, a design that doesn't expose pointers in their public interface at all is better by far.
:thumb:Quote:
As a consequence of this claim it follows that the primary way of dealing with polymorphic objects ( always according to tilavirga's reasoning ) is via reference semantics.
:thumb: :thumb:Quote:
Conversely, I claimed that reference semantics is intrinsically more complex than value semantics ( given two otherwise identical interfaces ) and that if we want to pass an object of type T,
---> responsabilities of the "handle" --->
T, T const&, T&, unique_ptr<T>, weak_ptr<T>, shared_ptr<T>, T const*, T*
we should always choose the type with the the smallest responsability compatible with the interface semantics.
but you forgot const versions of the wrapped pointers :p unique_ptr<const T> etc.
Although to be fair, I would probably also argue that if you have to go beyond T& in your public interface...rethink your design, there is probably a better alternative that doesn't need you to expose the (smart) pointer towards your class consumers.
THere's also more at stake than just responsability. If you need your 'reference' to be mutable or need it to be able to 'point nowhere', then neither T, T const& or T& will do. This is the 'issue" I have with most uses of pointers... when I see a pointer being used, I automatically assume that "it could change" (unless the (smart) pointer is const) or "it could be null", so I expect code to take proper precautions to guard against that. If the code then proceeds to assume it isn't null or doesn't mutate... "why did they use pointers". pointers add confusion and require extra attention a lot of code looks so much easier to read without them.
but as said before, you can't always avoid them
Are pointers evil? Not for the enlightened...
They are here though:
http://forums.codeguru.com/showthrea...g-Can-t-see-it
(Also pretty badly abused)
I agree that reference semantics can mean both pointers and references, but my issue is with lumping reference and pointer types under the term of "pointers". My reasoning is doing so is opens up liability for large misunderstandings. The word "pointer" in C++ texts is more often than not associated with the pointer type rather than its semantic meaning (in the latter cases it is generally explicitly specified or clearly inferred by context). Therefore most people when they read the term "pointers" are thinking of pointer types and not reference types unless otherwise specified.
In the context of interfaces, we need to be especially careful in order to avoid misunderstanding. After all, technically we cannot accept pointers in the term "pass by reference", but instead include them in the category of "pass by value" even though they refer to something else. Thus, it is dangerous IMO to include pointers under the banner of reference semantics in the context of parameter passing on interfaces because in this context they are value types. That said, when specifying interfaces, the terms, value, pointer and reference all have their own specific meaning - interchanging them only opens up the floor to misunderstanding.
Although I do not interpret Stroustup's text in the same way you have (I believe when he says "if Image had reference semantics" that he is referring to the internal design of the class), I have no argument with describing pointers as having [some] reference semantics - they do have some reference semantics, they just can't be called reference types because they do not fully adhere to reference semantics. They are values in their own right that can be directly manipulated separately to the objects to which they refer.
Yeah, it would have saved some time :)
Superbonzo (and OReubens), thanks for the clarification. :)
To be honest, as a general rule I tend to pass built-in types by value, but class types I prefer to pass by reference. That way I avoid potential object slicing issues and get a more efficient pass of the data.
Just to clarify (:D) this. In c++ you can pass a pointer by value or by reference. :)
Lol, very true :)
Agreed, though the alternative would be the more general term "reference", which of course has the potential for confusion because it does not mean the same thing as a C++ reference. This is precisely what happens when we use the term "reference semantics" without specifying what exactly is meant by "reference semantics" in a language that has the notion of both pointers and a reference feature.Quote:
Originally Posted by PredicateNormative
Yes, that is what he meant, otherwise his statement that "the code above would incur only the cost of a copy constructor call" would not make sense, i.e., he meant that there would still be the cost of a copy constructor call (recall this was written before move semantics came into play), but the image itself would not be copied, hence there is reference semantics rather than value semantics.Quote:
Originally Posted by PredicateNormative
Since his example is that of the Image class itself modified to have reference semantics, it follows that in his previous sentence concerning "use a container of handles or a containers of pointers", pointers (and "handles", which could well include the modified Image class example) have reference semantics with respect to the original Image class, i.e., although the pointer would be copied, the image itself would not be copied, hence there is reference semantics rather than value semantics.
No: the problem is that when you say "reference semantics", you're thinking of C++ references. Therefore, pointers obviously do not have reference semantics, and indeed they mean different concepts, related though they may be. But if you were to think of references in a more general, computer science, sense, then pointers are references, so pointers have reference semantics by definition.Quote:
Originally Posted by PredicateNormative
I don't think that's the case, though! It's quite a semantics game we're playing here. :D
I was under the impression that PredicateNormative and others who were insisting on the distinction were talking about references in the more general, computer science, sense. When thinking about reference semantics in that sense, we can ignore the distinction between C++ pointers and C++ references, and even smart pointers (the language constructs that are used to actually implement the computer science notion).
The point they were making is that it is the referenced object (object being pointed to) that conceptually has reference semantics (in the general CS sense), not the C++ pointer itself (or any equivalent implementation device) - as it is just a value that is used to refer to something else (and in this sense, it's no different than, say, an index into an array). (Side note: I'm talking on a base-case conceptual level, ignoring, for the purpose of the discussion, various cases were there can be multilevel indirection - from pointers to pointers, to stuff like virtual memory.)
Now, I don't disagree with superbonzo's constatation that introducing a level of indirection adds some complexity, but note a few things. Whether this extra complexity is worth it compared to the benefits is a design problem; if you misuse value semantics to implement something that is better done the other way, then you'll have to deal with the extra complexity that arises from the fact that you used the "wrong" tool to solve the problem. So, I'm not saying value & reference semantics are equally adequate/safe (at the design level) for the same types of problems, I'm just saying that some problems are better solved with one, and some are better solved with the other, and that I don't see one as particularly more "evil" than the other; I'm also saying that the tools that a particular language gives us may have properties of their own (in addition to the properties of the val/ref notions themselves) that may affect how we make that decision.
BTW, the reason this distinction is of certain practical importance, instead of being purely philosophical, is, for example, that you can, legitimately, use pointers (the language feature) to implement value semantics for the referenced objects.
Consider a class that is relatively expensive to copy, and possibly to instantiate and/or mutate, but that should have value semantics for one reason or another. Depending on the exact nature of the problem, if you make the class immutable, and add all new instances to a pool of some sort, you can mitigate some of these costs by working with references/pointers/handles to the objects in this pool. (Ignore memory management details of the pool itself for now.) All variables that should have the same state would actually be pointers (or something equivalent) referring to the same object. Whenever there's a need for a different state, a different instance must be created (even if it's done via member functions on the object itself).
For the outside observer this is, for all intents and purposes, value semantics - because, if you aren't actively trying to circumvent this design, (1) two objects with the same state are indistinguishable - their state, or value, is their identity, and (2) whenever you need to change something, a different instance must be created, and (3) although you are passing these objects around by reference, they are immutable, and you can't change them - all you can change is the variable (in this case - the pointer, which is passed by value), so that it points to a different instance.
I've been a little fast and loose with the terms in (1) when I wrote "two objects with the same state", so, to clarify that: if you think of pointers as of variables in the sense described in (3), then what I said in (1) is that, if you have two such variables (and if you don't rely on checking the memory addresses they store), you can compare their equality by value (that is, by comparing states) - as opposed to reference semantics, where two objects can have exactly the same state but different identities.
So, while the details of what's going on under the hood are somewhat different, all the essential features of value semantics are there.
I'm not so sure about that. I think I've been more careful than most not to confuse general computer science terms with specific C++ features.
I prefer a more established terminology. Have a look for example at a couple of quotes from Stroustrup when describing reference semantics and value semantics in general terms (my emphasis):Quote:
You might find it more helpful to use the same definitions that others on this forum are using; i.e. the language independent definitions derived from “pass by value” and “pass by reference” semantics - with the understanding that pointers fall under the category of pass by value. If you follow this definition then you will not incorrectly lump pointers and references together under the umbrella of the term "pointers" – pointers and references semantically different. Really, when it comes down to it, semantically, a reference should not be thought of as an object, but as an alias regardless of how a particular language implements it (a pointer on the other hand should be thought of as an object in its own right).
"With reference semantics, assignment is a pointer-copy (i.e., a reference). Value (or “copy”) semantics mean assignment copies the value, not just the pointer."
"Pros of reference semantics: flexibility and dynamic binding (you get dynamic binding in C++ only when you pass by pointer or pass by reference, not when you pass by value)."
Specifically note how reference semantics is described in terms of pointer and reference interchangeably. That's okay because they are semantically equivalent. If we on the other hand were talking about the C++ pointer type specifically then to say pass by pointer would be a misnomer. It's because C++ has two parameter passing mechanims only, by-value and by-reference (lvalue or rvalue), and a C++ pointer must be passed by either of these just like any type must.
Well, anyone who has read my posts will know by now that I agree with that. In fact it's my point.Quote:
Thus statements like “pointers are evil” refer to raw pointers (e.g. int* ptr) as inherited from C, not smart pointers, and certainly not references. As others have stated, that does not mean that raw pointers shouldn’t be used, but raw pointers should only be used when necessary – references or smart pointers should be preferred.
The problem is that most posts indicate the opposite. The general consensus seems to be that pointers in any form are evil, period. And when the poster who introduced the evil-pointer dogma in this thread claimed that also recursion is evil in another thread I finally realised I was prisoner of the Borg and had to flee or be assimilated. Resistance is futile. :)