This tutorial says,
You're probably noticing a similarity to pointers here--and that's true, references are often implemented by the compiler writers as pointers
In similar, one commented in
What is a reference variable in C++?
as
Technically not. If bar was a variable you could get its address. A reference is an alias to another variable (not the address of as this would imply the compiler would need to insert a dereference operation). When this gets compiled out bar probably is just replaced by foo
Which statement is true?
Both are true, but under different circumstances.
Semantically, a reference variable just introduces a new name for an object (in the C++ sense of "object").
(There's plenty of confusion around what "variable" and "object" mean, but I think that a "variable" in many other languages is called an "object" in C++, and that's what your second quote refers to as a "variable".)
If this reference isn't stored anywhere or passed as a parameter, it doesn't necessarily have any representation at all (the compiler can just use whatever it refers to instead).
If it is stored (e.g. as a member) or passed as a parameter, the compiler needs to give it a representation, and the most sensible one is to use the address of the object it refers to, which is exactly the same way as pointers are represented.
Note that the standard explicitly says that it it unspecified whether a reference variable has any size at all.
The C++ Standard states, at §8.3.2/4:
It is unspecified whether or not a reference requires storage.
And this non-specification is the main reason why both a pointer implementation and an aliasing implementation are valid implementations.
Therefore, both can be right.
They're both true, in a manner of speaking. Whether a reference gets compiled as a pointer is an implementation detail of the compiler, rather than a part of the C++ standard. Some compilers may use regular pointers, and some may use some other form or aliasing the referenced variable.
Consider the folowing line:
int var = 0;
int &myRef = var;
Compiler "A" may compile myRef as a pointer, and compiler "B" might use some other method for using myRef.
Of course, the same compiler may also compile the reference in different ways depending on the context. For example, in my example above, myRef may get optimized away completely, whereas in contexts where the reference is required to be present (such as a method parameter), it may be compiled to a pointer.
Related
From what I can tell, references can be used wherever the original type can (I'm not implying the reverse is true), the only difference is their mutation semantics (when the variables are used as lvalues).
Wouldn't they then qualify as the same type as the original? If so, why is the fact that something is a reference, stored in its type?
Edit: if references are a different type, why can they be substituted for the original type in so many situations, without explicit casting? Is there implicit cast involved?
Example:
void bar(int& a);
int x;
int& y = x;
bar(y) // matching type
bar(x) // what happened here? was x cast to a reference?
A reference is formally a type; or at least you can read things like "if T is a reference type" in the C++ standard itself.
However, your question is perfectly legitimate in that references have very confusing semantics. They are not quite first-class types (for example, you can't have a reference-to-reference or a pointer-to-reference), and in my opinion that's because C++ managed to conflate two different kinds of types with how it defines and uses references.
What a reference really does is it gives an alternate name to an already-existing object (value). What does this mean? It means that it doesn't "qualify" the type of the value it refers to per se; it rather qualifies the name ("variable", "storage") itself that is used for referencing the value.
In C++, the semantics of a type and a value often depends on additional properties of the storage where the object/value is stored. This is not always explicit, and that's what confuses people.
I think because C++ heavily relies on exposing the concept of "storage" (rather than hiding it as an implementation detail), there really should be two type systems: one for pure values themselves, and one for storage, where the type system for storage should be a superset of the type system for values.
Another example where a very similar issue appears is CV-qualification. It's not an object/value itself that should be const or volatile. It's the storage containing that value that may or may not be mutable, and may or may not need to be protected from certain load/store optimizations. Again, this could be better expressed if there was a way to express these properties of types separately in the case of values and storage.
From what I can tell, references can be used wherever the original type can
That is simply not true.
Consider:
void foo(int x);
void bar(int& x);
foo(3);
bar(3); // whoops!
And how about this:
struct T
{
int& x;
};
It wouldn't make sense not to have a distinct type for references. This way, you get function overloading powers and every other benefit that the type system gives you.
You would otherwise need to invent some other mechanism to denote a thing as being a reference rather than a non-reference; surely the type system is the perfect mechanism to denote that?
int and int& are two distinct types.
” From what I can tell, references can be used wherever the original type can
No. A reference refers. You can think of it as a pointer in disguise.
” Are references separate types in C++?
Yes.
” If not, why are they written in the type?
That's just the syntax for specifying a reference type, using & as a type builder symbol. As another example, * is a type builder for pointers. Except for a limitation of type inference we could now replace that (1)impractical syntax with template syntax.
1) Both the creators of C and the creator of C++ have on several occasions described the original C declaration syntax as a “failed experiment”.
Unlike a pointer, a reference cannot be reseated; the address it is referencing cannot be changed. By like a pointer, the reference is useful when avoiding copying semantics, thus needing to create an alias to something that already exists... i.e., knowing it is a reference and not an object means the compiler knows not to copy the object at assignment or when passing through functions.
EDIT: regarding the updated questions, "if references are a different type, why can they be substituted for the original type in so many situations, without explicit casting? Is there implicit casting involved?" ... not casting, it is a reference so it simply gets "dereferenced" by "pointing" to the original object; it may help to just think of it as just a substitution name, or an alias, etc.
I am learning about references in C++. In particular, i have learnt that references are not actual objects. Instead they refer to some other object. That is, reference are just alias for other objects.
Then i came across this which says:
Important note: Even though a reference is often implemented using an address in the underlying assembly language, please do not think of a reference as a funny looking pointer to an object. A reference is the object, just with another name. It is neither a pointer to the object, nor a copy of the object. It is the object. There is no C++ syntax that lets you operate on the reference itself separate from the object to which it refers.
I get that the above quote means that we can't operate on the reference itself separate from the object to which it refers but it still seems to imply that "a reference is an object".
Also, i have come across the the sentence given below:
In ISO C++, a reference is not an object. As such, it needs not have any memory representation.
I don't have a link to this 2nd quote but i read it in one of SO's post somewhere.
My question is that assuming the second quote is also from the standard(which may not be the case), doesn't these 2 quoted statements contradict each other. Or at least the first quote is misleading. Which one is correct.
My current understanding(by reading books like C++ Primer 5th edition) is that references are an alias for objects. Which leads me to the thinking that they should not take any space in memory.
Important note: Even though a reference is often implemented using an address in the underlying assembly language, please do not think of a reference as a funny looking pointer to an object. A reference is the object, just with another name. ...
Notes are informal and usually should not to be interpreted as strict rules. If an interpretation contradicts with standard rules, then that interpretation is wrong.
References and objects are different kinds of entities. A reference is not an object distinct from the one that it names. It isn't possible to form a pointer to a reference. A "pointer to reference" isn't even a valid type category.
The note is trying to say that reference "is" the object which it names in the sense that using the reference is using the referred object.
I was thinking of confirming that whether or not references take any space
References take space or they don't take space. It's up to the language implementation to figure out whether it needs space in each case.
Standard quote:
[dcl.ref] It is unspecified whether or not a reference requires storage
Outside of standard specifications, if you want an example of reference using space, try adding a reference member to a class and you are very likely to observe that the size of the class increases.
since pointers take space then reference should also take space. ...
Pointers do take space in the abstract machine that the standard specifies. But if you never observe the storage, then it's entirely possible that the storage never exists in practice. A significant difference between references and pointers is that you cannot observe the storage of a reference directly.
Philosopher: "If a tree falls in an abstract machine and no one is around to observe it, does it have an effect?"
Optimiser: "Not if I can help it."
A reference provides another way of referring to an object. That's useful particularly when passing parameters by reference to functions. More formally, a reference is an alias that binds to a variable including, in this case, an anonymous temporary.
Fortunately we don't need to concern ourselves how they are implemented. That's the job of the compiler, and techniques vary. The C++ standard does not require them to occupy any memory.
There is a way of distinguishing reference types by the way. Non-separability is really more about not being able to bind the reference to any other variable. See
#include <iostream>
#include <type_traits>
int main() {
int a = 0;
int& ref = a;
std::cout << (
std::is_reference<decltype(ref)>::value ? "pay me a bonus\n" : "reformat my hard disk\n"
);
std::cout << (
std::is_reference<decltype(a)>::value ? "pay me a bonus\n" : "reformat my hard disk\n"
);
}
Note finally that &a and &ref must always be the same.
The first quote is really saying the reference is not separable from the object.
... still seems to imply that "a reference is an object".
It really implies that a reference is a non-separable, non-first-class alias for an object, exactly as you first said.
The difficulty with these discussions is that the standardese meaning of "object" is already different from the meaning used in most less-formal contexts.
Let's start simple:
int a;
Would often be described as declaring an integer object a, right? It actually
declares an integer object
binds the name a to that object in the appropriate scope
Now, if we write
int &b = a;
we could say that b is the object in the same way as we could say that a is the object. Actually neither are correct, but given that informal text already uses the latter, it's no worse.
We should instead say that the name b refers to the same object as the name a. This is exactly consistent with calling it an alias, but informal or introductory texts would seem pretty cumbersome if they wrote "... the integer object referred to by the name a ..." everywhere instead of just "the integer a".
As for taking space in memory ... it depends. If I introduce 100 aliases for a single object inside a single function I'd be really surprised if the compiler didn't just collapse them (although of course they might still show up in debug symbols). No information is being lost here by eliding the redundant names.
If I pass an argument by reference to a non-inlined function, some actual information is being communicated, and that information must be stored somewhere.
What a reference actually "is" doesn't make much sense: you could say it is the referenced object or that it is an alias to it, and these claims are both true in some sense.
int main()
{
int a(0);
int& ref(a);
ref = 1; // Will actually affect the value of a
return 0;
}
Let's go through this program line by line.
int a(0); allocates some memory (usually 4 bytes) on the stack to hold an integer.
int& ref(a); doesn't necessarily allocate memory, and wether it actually will is compiler-specific. In this sense, ref itself is not an object: it is simply an alias, another name, for a. This is what the second quote means by "a reference is not an object". (Please note that sometimes, when the what object is referenced can't be known at compile-time for example, a reference has to reserve additional space for the object's address. In these cases, references are just syntactic sugar for pointers.)
ref = 1; sets the value of a to one. In this sense, you can think of ref as being precisely the same object as a. Any operation "on" the reference will actually operate on the referenced object. This is what the first quote means by "It is the object".
It has been established (see below) placement new is required to create objects
int* p = (int*)malloc(sizeof(int));
*p = 42; // illegal, there isn't an int
Yet that is a pretty standard way of creating objects in C.
The question is, does the int exist if it is created in C, and returned to C++?
In other words, is the following guaranteed to be legal? Assume int is the same for C and C++.
foo.h
#ifdef __cplusplus
extern "C" {
#endif
int* foo(void);
#ifdef __cplusplus
}
#endif
foo.c
#include "foo.h"
#include <stdlib.h>
int* foo(void) {
return malloc(sizeof(int));
}
main.cpp
#include "foo.h"
#include<cstdlib>
int main() {
int* p = foo();
*p = 42;
std::free(p);
}
Links to discussions on mandatory nature of placement new:
Is placement new legally required for putting an int into a char array?
https://stackoverflow.com/a/46841038/4832499
https://groups.google.com/a/isocpp.org/forum/#!msg/std-discussion/rt2ivJnc4hg/Lr541AYgCQAJ
https://www.reddit.com/r/cpp/comments/5fk3wn/undefined_behavior_with_reinterpret_cast/dal28n0/
reinterpret_cast creating a trivially default-constructible object
Yes! But only because int is a fundamental type. Its initialization is vacuous operation:
[dcl.init]/7:
To default-initialize an object of type T means:
If T is a (possibly cv-qualified) class type, constructors are considered. The applicable constructors are enumerated
([over.match.ctor]), and the best one for the initializer () is chosen
through overload resolution. The constructor thus selected is called,
with an empty argument list, to initialize the object.
If T is an array type, each element is default-initialized.
Otherwise, no initialization is performed.
Emphasis mine. Since "not initializing" an int is akin to default initialing it, it's lifetime begins once storage is allocated:
[basic.life]/1:
The lifetime of an object or reference is a runtime property of the
object or reference. An object is said to have non-vacuous
initialization if it is of a class or aggregate type and it or one of
its subobjects is initialized by a constructor other than a trivial
default constructor. The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if the object has non-vacuous initialization, its initialization is complete,
Allocation of storage can be done in any way acceptable by the C++ standard. Yes, even just calling malloc. Compiling C code with a C++ compiler would be a very bad idea otherwise. And yet, the C++ FAQ has been suggesting it for years.
In addition, since the C++ standard defers to the C standard where malloc is concerned. I think that wording should be brought forth as well. And here it is:
7.22.3.4 The malloc function - Paragraph 2:
The malloc function allocates space for an object whose size is
specified by size and whose value is indeterminate.
The "value is indeterminate" part kinda indicates there's an object there. Otherwise, how could it have any value, let alone an indeterminate one?
I think the question is badly posed. In C++ we only have the concepts of translation units and linkage, the latter simply meaning under which circumstances names declared in different TUs refer to the same entity or not.
Nothing is virtually said about the linking process as such, the correctness of which must be guaranteed by the compiler/linker anyway; even if the code snippets above were purely C++ sources (with malloc replaced with a nice new int) the result would be still implementation defined ( eg. consider object files compiled with incompatible compiler options/ABIs/runtimes ).
So, either we talk in full generality and conclude that any program made of more than one TU is potentially wrong or we must take for granted that the linking process is 'valid' ( only the implementation knows ) and hence take for granted that if a function from some source language ( in this case C ) primises to return a 'pointer to an existing int' then the the same function in the destination language (C++) must still be a 'pointer to an existing int' (otherwise, following [dcl.link], we could't say that the linkage has been 'achieved', returning to the no man's land).
So, in my opinion, the real problem is assessing what an 'existing' int is in C and C++, comparatively. As I read the correponding standards, in both languages an int lifetime basically begins when its storage is reserved for it: in the OP case of an allocated(in C)/dynamic(in c++) storage duration object, this occurs (on C side) when the effective type of the lvalue *pointer_to_int becomes int (eg. when it's assigned a value; until then, the not-yet-an-int may trap(*)).
This does not happen in the OP case, the malloc result has no effective type yet. So, that int does not exist neither in C nor in C++, it's just a reinterpreted pointer.
That said, the c++ part of the OP code assigns just after returning from foo(); if this was intended, then we could say that given that malloc() in C++ is required having C semantics, a placement new on the c++ side would suffice to make it valid (as the provided links show).
So, summarizing, either the C code should be fixed to return a pointer to an existing int (by assigning to it) or the c++ code should be fixed by adding placement new. (sorry for the lengthy arguing ... :))
(*) here I'm not claiming that the only issue is the existence of trap representation; if it were, one could argue that the result of foo() is an indeterminate value on C++ side, hence something that you can safely assign to. Clearly this is not the case because there are also aliasing rules to take into account ...
I can identify two parts of this question that should be addressed separately.
Object lifetime
It has been established (see below) placement new is required to create objects
I posit that this area of the standard contains ambiguity, omission, contradiction, and/or gratuitous incompatibility with existing practice, and should therefore be considered broken.
The only people who should be interested in what a broken part of the standard actually says are the people responsible for fixing the breakage. Other people (language users and language implementors alike) should defer to existing practice and common sense. Both of which say that one does not need new to create an int, malloc is enough.
This document identifies the problem and proposes a fix (thanks #T.C. for the link)
C compatibility
Assume int is the same for C and C++
It is not enough to assume that.
One also needs to assume that int* is the same, that the same memory is accessible by C and C++ functions linked together in a program, and that the C++ implementation does not define the semantics of calls to functions written in the C programming language to be wiping your hard drive and stealing your girlfriend. In other words, that C and C++ implementations are compatible enough.
None of this is stipulated by the standard or should be assumed.
Indeed, there are C implementations that are incompatible with each other, so they cannot be both compatible with the same C++ implementation.
The only thing the standard says is "Every implementation shall provide for linkage to functions written in the C programming language" (dcl.link) What is the semantics of such linkage is left undefined.
Here, as before, the best course of action is to defer to existing practice and common sense. Both of which say that a C++ implementation usually comes bundled with a compatible enough C implementation, with the linkage working as one would expect.
The question is meaningless. Sorry. This is the only "lawyer" answer possible.
It is meaningless because the C++ and the C language ignore each others, as they ignore anything else.
Nothing in either language is described in term of low level implementation (which is ridiculous for languages often described as "high level assembly"). Both C and C++ are specified (if you can call that a specification) at a very abstract level, and the high and low levels are never reconnected. This generates endless debates about what undefined behaviors means in practice, how unions work, etc.
Although neither the C Standard nor, so far as I know, the C++ Standard officially recognizes the concept, almost any platform which allows programs produce by different compilers to be linked together will support opaque functions.
When processing a call to an opaque function, a compiler will start by
ensuring that the value of all objects that might legitimately be examined
by outside code is written to the storage associated with those objects.
Once that is done, it will place the function's arguments in places specified
by the platform's documentation (the ABI, or Application Binary Interface)
and perform the call.
Once the function returns, the compiler will assume that any objects which
an outside function could have written, may have been written, and will thus
reload any such values from the storage associated with those objects the
next time they are used.
If the storage associated with an object holds a particular bit pattern when
an opaque function returns, and if the object would hold that bit pattern
when it has a defined value, then a compiler must behave as though the object
has that defined value without regard for how it came to hold that bit
pattern.
The concept of opaque functions is very useful, and I see no reason that the C and C++ Standards shouldn't recognize it, nor provide a standard "do nothing" opaque function. To be sure, needlessly calling opaque functions will greatly impede what might otherwise be useful optimizations, but being able to force a compiler to treat actions as opaque function calls when needed may make it possible to enable more optimizations elsewhere.
Unfortunately, things seem to be going in the opposite direction, with build systems increasingly trying to apply "whole program" optimization. WPO would be good if there were a way of distinguishing between function calls that were opaque because the full "optimization barrier" was needed, from those which had been treated as opaque simply because there was no way for optimizers to "see" across inter-module boundaries. Unless or until proper barriers are added, I don't know any way to ensure that optimizers won't get "clever" in ways that break code which would have had defined behavior with the barriers in place.
I believe it is legal now, and retroactively since C++98!
Indeed the C++ specification wording till C++20 was defining an object as (e.g. C++17 wording, [intro.object]):
The constructs in a C++ program create, destroy, refer to, access, and
manipulate objects. An object is created by a definition (6.1), by a
new-expression (8.5.2.4), when implicitly changing the active member
of a union (12.3), or when a temporary object is created (7.4, 15.2).
The possibility of creating an object using malloc allocation was not mentioned. Making it a de-facto undefined behavior.
It was then viewed as a problem, and this issue was addressed later by https://wg21.link/P0593R6 and accepted as a DR against all C++ versions since C++98 inclusive, then added into the C++20 spec, with the new wording.
The wording of the standard are quite vague and may even seem to use tautology, defining a well defined implicitly-created objects (6.7.2.11 Object model [intro.object]) as:
implicitly-created objects whose address is the address of the start
of the region of storage, and produce a pointer value that points to
that object, if that value would result in the program having defined
behavior [...]
The example given in C++20 spec is:
#include <cstdlib>
struct X { int a, b; };
X *make_x() {
// The call to std::malloc implicitly creates an object of type X
// and its subobjects a and b, and returns a pointer to that X object
// (or an object that is pointer-interconvertible ([basic.compound]) with it),
// in order to give the subsequent class member access operations
// defined behavior.
X *p = (X*)std::malloc(sizeof(struct X));
p->a = 1;
p->b = 2;
return p;
}
It seems that the `object` created in a C-function as in the OP question, falls into this category and is a valid object. Which would be the case also for allocation of C-structs with malloc.
No, the int does not exist, as explained in the linked Q/As. An important standard quote reads like this in C++14:
1.8 The C ++ object model [intro.object]
[...] An object is created by a definition (3.1), by a new-expression (5.3.4) or by the
implementation (12.2) when needed. [...]
(12.2 is a paragraph about temporary objects)
The C++ standard has no rules for interfacing C and C++ code. A C++ compiler can only analyze objects created by C++ code, but not some bits passed to it form an external source like a C program, or a network interface, etc.
Many rules are tailored to make optimizations possible. Some of them are only possible if the compiler does not have to assume uninitialized memory contains valid objects. For example, the rule that one may not read an uninitialized int would not make sense otherwise, because if ints may exist anywhere, why would it be illegal to read an indeterminate int value?
This would be a standard compliant way to write the program:
int main() {
void* p = foo();
int i = 42;
memcpy(p, &i, sizeof(int));
//std::free(p); //this works only if C and C++ use the same heap.
}
I'm not asking the difference between pointer and reference. Just a bit confused about the difference between reference and alias.
As far as I'm concerned, reference is a data type while alias is just a word describing the utility of this data type?
Thanks!
Aliasing refers to any way to refer to the same data through different names. References and pointers are two ways of achieving this behavior.
No, a reference is not a data-type, a reference references some other variable. Using a reference is the same as using the variable it references. It's very similar to a pointer (and it's not unlikely that the compiler treats references as pointers under the hood).
I've never heard of "alias" by itself in the context of C++, but there are type-aliases, created by e.g. typedef or using. There's also aliasing which is unrelated to both references and type-aliases.
Sorry, you said you were not asking the difference between a pointer and a reference.
To answer what you’re asking, the word reference means that a variable is pointing to a location in memory. Alias has a few different meanings, but the one I’ve seen most often in this context is that more than one variable are referencing the same location in memory, such as when you try to call memcpy(p, p, n);. One way to make this happen is with a C++ reference, which is a term of art for a language feature similar but not identical to pointers. Not every reference in C++ necessarily refers to something which ever has another name. You can also do aliasing with pointers, the address operator, a call by reference, or the compiler merging constants so that "Hello" and "Hello" in two places point to the same bytes in memory, or unions. Probably not exhaustive.
If people want to call a reference to something an “alias” even when there isn't any other variable referencing the same memory at the same time, I’m not strongly motivated to argue.
As several others have pointed out, C++14 uses the term “alias” to refer to template types declared with using. (http://en.cppreference.com/w/cpp/language/type_alias)
A type alias declaration introduces a name which can be used as a synonym for the type denoted by type-id. It does not introduce a new type and it cannot change the meaning of an existing type name. There is no difference between a type alias declaration and typedef declaration. This declaration may appear in block scope, class scope, or namespace scope.
From http://en.cppreference.com/w/cpp/language/type_alias
From what I can tell, references can be used wherever the original type can (I'm not implying the reverse is true), the only difference is their mutation semantics (when the variables are used as lvalues).
Wouldn't they then qualify as the same type as the original? If so, why is the fact that something is a reference, stored in its type?
Edit: if references are a different type, why can they be substituted for the original type in so many situations, without explicit casting? Is there implicit cast involved?
Example:
void bar(int& a);
int x;
int& y = x;
bar(y) // matching type
bar(x) // what happened here? was x cast to a reference?
A reference is formally a type; or at least you can read things like "if T is a reference type" in the C++ standard itself.
However, your question is perfectly legitimate in that references have very confusing semantics. They are not quite first-class types (for example, you can't have a reference-to-reference or a pointer-to-reference), and in my opinion that's because C++ managed to conflate two different kinds of types with how it defines and uses references.
What a reference really does is it gives an alternate name to an already-existing object (value). What does this mean? It means that it doesn't "qualify" the type of the value it refers to per se; it rather qualifies the name ("variable", "storage") itself that is used for referencing the value.
In C++, the semantics of a type and a value often depends on additional properties of the storage where the object/value is stored. This is not always explicit, and that's what confuses people.
I think because C++ heavily relies on exposing the concept of "storage" (rather than hiding it as an implementation detail), there really should be two type systems: one for pure values themselves, and one for storage, where the type system for storage should be a superset of the type system for values.
Another example where a very similar issue appears is CV-qualification. It's not an object/value itself that should be const or volatile. It's the storage containing that value that may or may not be mutable, and may or may not need to be protected from certain load/store optimizations. Again, this could be better expressed if there was a way to express these properties of types separately in the case of values and storage.
From what I can tell, references can be used wherever the original type can
That is simply not true.
Consider:
void foo(int x);
void bar(int& x);
foo(3);
bar(3); // whoops!
And how about this:
struct T
{
int& x;
};
It wouldn't make sense not to have a distinct type for references. This way, you get function overloading powers and every other benefit that the type system gives you.
You would otherwise need to invent some other mechanism to denote a thing as being a reference rather than a non-reference; surely the type system is the perfect mechanism to denote that?
int and int& are two distinct types.
” From what I can tell, references can be used wherever the original type can
No. A reference refers. You can think of it as a pointer in disguise.
” Are references separate types in C++?
Yes.
” If not, why are they written in the type?
That's just the syntax for specifying a reference type, using & as a type builder symbol. As another example, * is a type builder for pointers. Except for a limitation of type inference we could now replace that (1)impractical syntax with template syntax.
1) Both the creators of C and the creator of C++ have on several occasions described the original C declaration syntax as a “failed experiment”.
Unlike a pointer, a reference cannot be reseated; the address it is referencing cannot be changed. By like a pointer, the reference is useful when avoiding copying semantics, thus needing to create an alias to something that already exists... i.e., knowing it is a reference and not an object means the compiler knows not to copy the object at assignment or when passing through functions.
EDIT: regarding the updated questions, "if references are a different type, why can they be substituted for the original type in so many situations, without explicit casting? Is there implicit casting involved?" ... not casting, it is a reference so it simply gets "dereferenced" by "pointing" to the original object; it may help to just think of it as just a substitution name, or an alias, etc.