views:

425

answers:

6

For example: void foo( int& i ); is not allowed. Is there a reason for this, or was it just not part of the specification? It is my understanding that references are generally implemented as pointers. In C++, is there any functional difference (not syntactic/semantic) between void foo( int* i ) and void foo( int& i )?

+31  A: 

Because references are a C++ feature.

dan04
Is there any reason, apart from "that's just the way it is", they're not a C feature also?
That's just the way it is
Paul
There are reasons C programmers tend to be quite happy not having references in C.
R..
@Paul: Well, that, and also the fact that was C++ was invented after C.
Oli Charlesworth
dan04
@dan04: Ideally C++ programmers would only use references for those two purposes, but in reality I see them recommending references (even for questions clearly tagged C) all the time when a pointer would be the correct approach for either language...
R..
@user478719: The whole point of C still existing as a distinct language from C++ is its minimalist design (and resulting benefits like faster compile times and more standardized object layout). If you want C++ features, you can just use C++.
dan04
@dan04: "Minimalist design" is not a justification for them being distinct languages. If that were the case, you could just use a minimal subset of C++ and be happy without C. On the other hand, what I want is sanity - things like void pointers that automatically convert to/from any pointer type, which C++ ruined. Basically any C code that isn't littered with countless useless casts spews errors if you try to compile it as C++.
R..
@R.. Good C++ code should have no pointers and no casts.
Philipp
@Philipp That is just... *so* silly. My mind boggles.
meagar
@Philipp Good C++ code should start with template <int CONDITION>, and then you grab the ball and run... no pointers no casts.
Captain Giraffe
Also me not knowing C is there anyway to push a huge object with a function argument onto the stack in c?
Captain Giraffe
the reason why c++ references are nice when pointers 'would do' is that it prevents even the possibility of a null pointer. references cannot be null. but that's c++, in C you don't have references...
TokenMacGuy
@meagar : Mine is ok with the "Good C++ code should have no pointers and no casts". It is somewhat extreme, but pointers are to be limited to cases where you have no other choice. As for casts, usually, in C++, they are proof there are something wrong either with your code, or with the API you're using.
paercebal
@R.. : "things like void pointers that automatically convert to/from any pointer type, which C++ ruined." As a C++ developper, I'm very very happy the compiler refuses the compilation of a cast from `void *` to any pointer. In C++, there are so few needs for those casts the error seldom happens anyway, and when it happens, I want them corrected with a `static_cast`. Those casts are not useless because they mark a weakness in your program. This is all about strong type safety.
paercebal
@meagar: I write lots of C++ code and pointers are hidden way down deep. There is probably no pointers in any major logic. The reason being is that in C++ we invented this concept called `Ownership Symantics`. This is where dynamically allocated objects have an explicit owner. Then the owner does all the memory clean up and removes all those nasty memory leaks that are the hallmark of C code (beginner C code). Pointers do not work for `Ownership Symantics` as there is no indication of who owns a pointer its just a pointer.
Martin York
@R..: "Ideally C++ programmers would only use references for those two purposes". Wrong. The "two purposes" are historical purposes from 25 years ago. Things evolved, and now references are to be used whenever possible instead of pointers. This is a good thing, and it got even extended, as C++0x even added r-value references to implement move semantics (available in gcc 4.4 and msvc2010). All this to say there are a lot less good reasons to use pointers in C++ than in C, and this number of good reasons is steadily decreasing... And again: In C++, this is a good thing.
paercebal
@R..: If you have lots of casts in your code then I would suggest you are not writing C++ (in the modern sense of the language). You are writing in a language refereed to as `C with classes`. It is hallmark of programmers who know C but have not quite grokked how to use the more expressive syntax/style of C++. In interviews it is easy to spot. It is easy to move from C -> C with classes. It is a completely different thing to move to using C++.
Martin York
Arg what kind of rabid C++ responses have I provoked? My point from the beginning is that a "minimal subset of C++" does not make a useful replacement for C. I have no interest in writing C++ code, nor in trying to write C code that compiles as C++.
R..
@R..: rabid C comments provoke rabid C++ comments. Surprised? Saying "my language got it right, and yours didn't" tends to lead to people saying "nuh-uh!" -- apart from that, there are reasons why C programmers tend to be happy about *everything* their language doesn't have. There are reasons why, 40 years ago, programmers were happy their language didn't have functions.
jalf
A: 

Because the & operator has only 2 meanings in C:

  1. address of its operand (unary),

  2. and, the bitwise AND operator (binary).

int &i; is not a valid declaration in C.

Kedar Soparkar
`int ` is not a valid "declaration" in C++ either!
André Caron
@Andre: Given an initializer it's fine.
GMan
+10  A: 

References are merely syntactic vinegar for pointers. Their implementation is identical, but they hide the fact that the called function might modify the variable. The only time they actually fill an important role is for making other C++ features possible - operator overloading comes to mind - and depending on your perspective these might also be syntactic vinegar.

R..
I agree, however C++ also has const. Declaring functions that take const references to objects is 1. much safer than using pointers (a reference can't be null) and 2. easier to read, because you don't have to dereference the reference using * or ->
Orion Edwards
dan04
References do not provide you safety against NULL pointers. Consider `foo(*(int *)0);` or `foo(*(int *)rand())`. The compiler can do some tests against this, but not if the code is `foo(*p)` and `p` just happens to be `NULL`. By the way, C can provide the same level of halfway-enforcement against NULL pointers by declaring the function as `void foo(int i[static 1]);`
R..
@R: Invoking undefined behavior to make a point misses the point.
GMan
@GMan: Passing a NULL pointer to a function which will dereference the pointer and passing a dereferenced NULL pointer to a function that takes a references are **both equally undefined behavior**. My point is that references do not protect your function any more from NULL pointers than just passing a pointer would.
R..
That is a very C view on references (as the way references are implemented under the hood is via pointers). But I would say the references are a completely different animal and that without them C++ would be as crippled as C when it comes to important C++ idioms. PS. You are using C-Casts to generate invalid references. The C cast allows you to do anything. At least with rinterpret_cast<> the next person to read your code could easily spot it and say "Hay idiot" those reinterpret_casts are stupid. And why are you passing pointers anyway they ar not safe pass references.
Martin York
@R: The location of the UB is at the point you dereference null. By taking a reference instead of pointer, I guarantee my function only operates on valid objects. If the caller dereferences null, then regardless of anything else the UB is *not* the fault of my function. Contrarily, when you accept pointers instead of references, you make it your responsibility to correctly check the pointers aren't null and report the error. With references, this responsibility with managing pointers (correctly) lies with the code using the pointers, and not the function.
GMan
The implementation of references and pointers isn't usually identical in the compiler. A pointer is a memory address, a reference is an alias for another object. And I can pass a null pointer to a function without invoking UB. I can never pass a "null reference" without it being UB.
jalf
@GMan: That is utter rubbish. Why should a function that takes a pointer be expected to accept invalid pointer values? Why should `foo` have to check that it wasn't called as `foo((int *)0)` when it can't also check that it wasn't called as `foo((int *)1)` or any of the other billions of possible invalid pointer values? Unless a function's documentation **specifically** states that it accepts certain invalid pointer values and endows them with special meaning, the idiot writing the code that calls such a function with a null pointer argument is the one *responsible* for invoking UB.
R..
As an aside: as I already stated, declaring `void foo(int i[static 1]);` is a perfectly good way of documenting at the code level that the pointer passed must actually point to an object (and thus cannot be NULL, uninitialized, pointing one past the end of an array, etc.).
R..
@R: It's not that it should expect them, it's that it *can*. And to be defensive, one should put checks in place; even if that doesn't stop every problem, it's better than none. If you want to go the documentation route, think of a reference as a self-documenting tool. It says that this function argument is an alias for a value, so don't pass me any other thing. A pointer does no such thing. I agree there are other ways to solve it as you show, and a reference is another way.
GMan
+4  A: 

For example: void foo( int& i ); is not allowed. Is there a reason for this, or was it just not part of the specification?

It was not a part of the specification. The syntax "type&" for references were introduced in C++.

It is my understanding that references are generally implemented as pointers. In C++, is there any functional difference (not syntactic/semantic) between void foo( int* i ) and void foo( int& i )?

I am not sure if it qualifies as a semantic difference, but references offer better protection against dereferencing nulls.

Ziffusion
A: 

For a function argument, the difference between pointer and reference is not that big a deal, but in many cases (e.g. member variables) having references substantially limits what you can do, since it cannot be rebound.

DeadMG
A: 

References were not present in C. However, C did have what amounts to mutable arguments passed by reference. Example:
int foo(int in, int *out) { return (*out)++ + in; }
// ...
int x = 1; int y = 2;
x = foo(x, &y);
// x == y == 3.
However, it was a common error to forget to dereference "out" in every usage in more complicated foo()s. C++ references allowed a smoother syntax for representing mutable members of the closure. In both languages, this can confound compiler optimizations by having multiple symbols referring to the same storage. (Consider "foo(x,x)". Now it's undefined whether the "++" occurs after only "*out" or also after "in", since there's no sequence point between the two uses and the increment is only required to happen sometime after the value of the left expression is taken.)

But additionally, explicit references disambiguate two cases to a C++ compiler. A pointer passed into a C function could be a mutable argument or a pointer to an array (or many other things, but these two adequately illustrate the ambiguity). Contrast "char *x" and "char *y". (... or fail to do so, as expected.) A variable passed by reference into a C++ function is unambiguously a mutable member of the closure. If for instance we had
// in class baz's scope
private: int bar(int &x, int &y) {return x - y};
public : int foo(int &x, int &y) {return x + bar(x,y);}
// exit scope and wander on ...
int a = 1; int b = 2; baz c;
a = c.foo(a,b);
We know several things:
bar() is only called from foo(). This means bar() can be compiled so that its two arguments are found in foo()'s stack frame instead of it's own. It's called copy elision and it's a great thing.

Copy elision gets even more exciting when a function is of the form "T &foo(T &)", the compiler knows a temporary is going in and coming out, and the compiler can infer that the result can be constructed in place of the argument. Then no copying of the temporary in or the result out need be compiled in. foo() can be compiled to get its argument from some enclosing stack frame and write its result directly to some enclosing stack frame.

a recent article about copy elision and (surprise) it works even better if you pass by value in modern compilers (and how rvalue references in C++0x will help the compilers skip even more pointless copies), see http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ .

Eric Towers