A stupid question...probably

This is a discussion on A stupid question...probably within the C++ Programming forums, part of the General Programming Boards category; A stupid question then...
I was wondering, in C when you wanted to pass an argument in a function that ...

A stupid question...probably

A stupid question then...
I was wondering, in C when you wanted to pass an argument in a function that would take the changes within the function and retain them (that is not a copy) you would pass a pointer to it.

In C++ there is this by reference thing where when you declare your function you declare the argument in discussion something like this void f(int&....) right? Then if you change that int in the function when you return to the main program it has also changed... (Personally i think it;s a lot easier than the pointers so I was wondering why the two was of refernce in C++ if someone can answer me in this as well...)

The stupid question. If I have a pointer to struct and I want to pass it in an argument I would probably do it like a pointer to pointer in C - which makes it more confusing - so can i pass it like this in C++ void f(node*&, ....)

Actually I tried it and I think it worked although I didnt check it out thoroughly cause I was doing something else and I lost that program. So now I am doing something else where I work with pointers and if this thing works it would help that having a function with an argument like this node*** (I have a pointer which I pass into a function and then on to another function)

So to get a pointer to a pointer, you just have a pointer type and take the address of it with the & operator.

The C++ principle of references is just pointers in disguise - the compiler will translate a reference into a pointer behind the scenes. It makes a difference when it comes to how you use the variable that is a reference, vs. the use of a pointer (e.g. *ptr = something, ref = something, ptr->someitem, ref.someitem), but there is really no difference in the machine code generated by the compiler.

Edit, and yes: you can use func(node * &ptr) in C++ if you like - it would be a little bit confusing, but sure, you can do that.

"Simplicity does not precede complexity, but follows it." -- Alan Perlis
"Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
"The only real mistake is the one from which we learn nothing." -- John Powell

Also note that a reference is not always a pointer. It is, quite literally, an alias, and so in many cases does not involve a pointer.

Which means, basically that in this example:

Code:

int x = 0;
int &y = x;
y = 1;

The compiler is not necessarily going to create a pointer to x and then dereference it on the line "y = 1." It is trivial to analyze the situation and see that you can just set x directly. No pointer is necessarily involved.

This example seems contrived, but when you get into situations with nested inline functions a lot of references can just "boil away." They are NOT pointers.

>but when you get into situations with nested inline functions a lot of references can just "boil away."
It's not even that specialized. There are tons of times when references can just become the original object, depending on how the compiler handles symbols.

The compiler is not necessarily going to create a pointer to x and then dereference it on the line "y = 1." It is trivial to analyze the situation and see that you can just set x directly. No pointer is necessarily involved.

This example seems contrived, but when you get into situations with nested inline functions a lot of references can just "boil away." They are NOT pointers.

True - in the case of inline functions the "reference" becomes the oriignal variable with a different name. But then that can be said about other inline cases too - I'm pretty sure that if you do:

Code:

int foo(int *x) {
*x = 7;
}
int main()
{
int a;
foo(&a);
}

the compiler may well not produce any pointer to a at all - because it can analyze the situation and realize that it can just set a to 7.

So, when references are really used (and not optimized away) they are essentially pointers.

So, when references are really used (and not optimized away) they are essentially pointers.

If that's taken as true, then they are pointers that are always valid. They're never NULL or out-of-scope or dangling. At least, not very often. You could probably get a dangling reference if you tried hard enough.

"Simplicity does not precede complexity, but follows it." -- Alan Perlis
"Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
"The only real mistake is the one from which we learn nothing." -- John Powell

If that's taken as true, then they are pointers that are always valid. They're never NULL or out-of-scope or dangling. At least, not very often. You could probably get a dangling reference if you tried hard enough.

"Simplicity does not precede complexity, but follows it." -- Alan Perlis
"Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
"The only real mistake is the one from which we learn nothing." -- John Powell

References, unlike pointers, aren't objects. That's very important when you argue about the legality of programs under the strict terms of the standard - in particular, whether a program violates the ODR or not.

A trick used in some Boost libraries depends on this. Let's see if I remember.

The problem is this: consider a library that requires some complex, predefined object to be available to the programmer. Let's say, Boost.Lambda and its placeholders, _1, _2 etc.
Lambda is a header-only library. There are no source files to place definitions in. This is a problem, because objects have to be defined. But if you place the definitions in header files, you'll get redefinition linker errors.
No problem, you may think, I'll just use an unnamed namespace (you've considered making the objects static, too, but static is just an inferior method of achieving the same; the entire below discussion applies to static stuff, too), and all problems will go away:

Not right. Aside from several compilers having trouble with unnamed namespaces in header files due to precompiled headers, there's also a language standard issue. What if the user (or the library, for that matter) has an inline function in a header?

With inline functions, the compiler (or linker, but the language definition calls it all the implementation) has to accept that every file contains a definition of the function. It has to collapse these definitions, and use just one. However, that means that all definitions must be exactly the same.

where unique_name is a name that is unique to each translation unit. This means that, since code outside the translation unit can't recreate the name, they cannot refer to elements of the namespace (except indirectly, through pointers, references and template parameters).

The net effect of this is that in translation unit 1, the placeholder objects are boost::lambda::unique1::_1 etc., whereas in translation unit 2, the placeholder objects are boost::lambda::unique2::_1 etc. The inline function foo(), present in both translation units, refers to the unique1 variants of the placeholders in translation unit 1, to the unique2 variants in the other. In other words, the definition of foo() differs between the translation units; the ODR has been violated.

Does it matter in the practical world?

Apparently. Boost enforces a rule that unnamed namespaces are disallowed in their headers. There's a real reason for that, not just some language lawyer stuff. We've had problems, though I can't remember what they were.

Sooooo ... where do references come in?

Well, if we can't use unnamed namespaces, we've got to find another way. There's another thing that the compiler collapses between translation units, and that's templates. You can put a template definition in a header file - in fact you have to - and the compiler will accept it, no matter in how many files the definition is. So you could declare the placeholders like this:

OK, but now the syntax is awkward. Not only does client code have to refer to the placeholders as, for example, placeholders<int>::_1, but you actually have to enforce the same type, because otherwise the objects aren't the objects your library uses, and it all wouldn't work.
The second is easy to deal with.

Code:

typedef placeholders<int> args;

Now it's args::_1 and args::_2. Better. Still, not quite satisfactory.

Because references aren't objects, they're just object aliases. And in the definition of an inline function, the actual objects count, which are the same for all translation units, even if the references are different.

Admittedly, it's a grey area of the standard. You can make a case for both interpretations. But that's enough - together with the fact that the real-world problems didn't occur with this solution - to make Boost accept this variant.

Phew, that was long and useless. But it should show that there are differences between pointers and references beside syntax.