What is the difference between:
const double& pi = 3.14;
and (no ampersand):
const double pi = 3.14;
They both seem to have the same L and R values so what is the difference?
For your particular example there's no difference.
And that means, no way to tell them apart, whatsoever.
However, since the first binds a reference to a temporary, when the type is of class type the temporary can be of a derived class, e.g. produced by a function! And it then has its destructor properly called at the end of the scope. This little el neato trick is used in ScopeGuard implementations (see the original ScopeGuard article in DDJ, by Petru Marginean and Andrei Alexandrescu -- Petru invented ScopeGuard and Andrei made a more general thing on top).
I once asked Bjarne Stroustrup, who created the C++ language, why the syntax in your first declaration is supported.
And his reply was that it was mostly to have uniform rules (i.e. to not make any special exception for local references as opposed to references as formal parameters). I think at that time neither of us were familiar with ScopeGuard. It's simple in retrospect, but it takes a mind like Petru's, or Andrei's, to come up with something like that! :-)
Cheers & hth.
The important difference with a reference is that a reference itself is inheritly constant. Once the reference itself has been initially assigned to a variable, it can not then reference another variable. All attempts to modify it will modify the variable it refers to. Given this, the const will mean that the reference is a reference to a const int.
const int A;
const int B;
const int& Reference = A;
Reference = B; // Error, the value of A can not be assigned, nor would this *ever* be able to make Reference refer to B.
You can also test this theory about a reference itself being constant like so:
const int& const Reference; // Should give a warning about the second const being redundant.
A bit of clarification about constant references, references and constants for doubles.
Reference
A reference refers to an existing an object and cannot be reseated. That is, once you declare (define) the reference, it will always refer to that item.
Constant Reference
The C++ language allows for declaring of a constant reference. This tells the compiler that the reference will not change. This may be redundant since references cannot be reseated. However, the language syntax allows it.
Constant
A constant is a value, and does not refer to anything.
Optimizations & Substitutions
The compiler is allowed to substitute (replace) a reference to an object, constant or literal with the corresponding object, constant or literal, provided that the compiler can guarantee that no write operations are performed to that object within the scope it is used in. This determination may become difficult when the reference is passed to methods or functions within that scope.
Specifying the const modifier to a reference will make the compiler's job easier for optimizing. The constant reference is a contract with the programmer and user that the reference will not be changed.
const double& is a reference to a constant double, the other one is a constant double. A reference is kind of a const pointer, a pointer that never changes.
In C++ the references are inherently const. Once they have been assigned you can not changes them. They must be both declared and initialized.
The reference isnt const only the value is const, so you should be able reassign referense, that means the following would be ok:
const double& pi = 3.14;
const double pi2 = 2.78;
pi = *(&pi2);
Related
In short: Does the following code have Undefined Behavior, or is this fine?
struct X
{
X(int b)
: value(b)
, ref(value)
{
}
int value;
int& ref;
void isThisUB() const
{
ref = 1;
}
};
int main()
{
const X x(2);
// Is either of these fine?
x.isThisUB();
x.ref = 3;
return x.value;
}
https://godbolt.org/z/1TE9a7M4a
X::value is const for x. According to my understanding of const semantics, this means that modifying it in any way is UB. Yet we can take a non-const reference to it in the constructor and then modify it through that, either in a const member function or directly.
The C++ (at least 17) standard gives an example of const-related UB in [dcl.type.cv] that looks mostly the same, except it employs const_cast. Note how p->x.j = 99 is denoted as UB. I do not see a fundamental difference between achieving this with const_cast vs my above code.
So, is the code above UB? Are non-const reference members/pointers really this big of a footgun?
(If you can come up with search keywords that yield a related question and not just random const stuff, I'll be mighty impressed.)
Does the following code have Undefined Behavior, or is this fine?
It has UB. Standard says:
[dcl.type.cv]
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
x is const and you modify its non-mutable member.
I do not see a fundamental difference between achieving this with const_cast vs my above code.
Indeed. Both are UB for the same reason.
Are non-const reference members/pointers really this big of a footgun?
The trigger for the footgun is the issue that the object is temporarily non-const while it is within its constructor. Hence pointers and references to non-const "this" and its subobjects are readily available wthin the constructor regardless of whether the object is going to be const or not. Thus we can conclude that storing those pointers/references for later use is ill-advised.
Storing pointers and references as members referring to "this" are a footgun for several other reasons as well. They require storage that's otherwise unnecessary if you were to access the referred member through its name directly. Furthermore, you'll find that the copy-semantics of the class will likely not be what you had in mind.
If you want to point to a member out of several alternatives, then use a member-pointer, not an object pointer / reference (using storage cannot be avoided for such case). This solves both copying and accidental const violation.
The book said: Because references are not objects, we may not define a reference to a reference.
int ival = 1024;
int &refVal = ival;
refVal = 2;
int ii = refVal;
int &refVal3 = refVal; // isn't this a definition of ref to ref?
int i = refVal;
int &refVal4 = 10;
double dval = 3.14;
int &refVal5 = dval;
However, that line is not an error, because refVal3 is saying it is just another alias for refVal, and refVal is just another name for ival(refVal3 is bound to the object to which refVal is bound to, which is ival)... so both refVal and refVal3 refer to the initializer of ival.
That makes perfect sense, but if that's not a definition of a reference to a reference, just what exactly does the book mean when it mentioned "Because references are not objects, we may not define a reference to a reference." ??
Can someone perhaps give me an example ?
Your understanding is correct.
int &refVal3 = refVal;
This makes refVal3 a reference to the same thing refVal is a reference to. That is, ival.
just what exactly does the book mean when it mentioned "Because
references are not objects, we may not define a reference to a
reference." ?
A reference can only refer to an object. But references are not objects. So a reference cannot refer to a reference. And when the book says this, it doesn't just mean that it's not allowed, and you'll get an error if you try. But you can't even try. There's no syntax for it. Because anything you try to do to a reference, you will actually be doing to the object it refers to, and not to the reference itself.
Can someone perhaps give me an example ?
No. I can't. And that's the point. There's simply no syntax for it.
Reference-to-reference types (like T & &) do not exist in C++.
Where T is an object type (which includes int, as in your example):
You can have (lvalue) references to T. There exists a type T &.
You can have rvalue references to T. There exists a type T &&.
T && is not a reference to a reference; && is a single token and does not mean & &.
You cannot have references to references to T. There are no such types as T & &, T & &&, T && & or T && &&. If you write a declaration that attempts to explicitly name such a type, this is an error.
(Similarly, cv-qualified types like const T& exist, while types like const T & & do not exist.)
You asked for an example. Consider this wrong code:
int main()
{
int ival = 1024;
int &refVal = ival;
int & &refRefVal = refVal; // wrong
}
This is an error because there is no such type as int & &. It would be an error regardless of what I tried to initialize it with.
(Strictly speaking, it is an error because the syntax of the language prohibits it. The standards committee could have chosen to allow me to write int & & and have it mean the same thing as int &--see Reference Collapsing below--but they didn't, which is good, because that would be very confusing.)
When I attempt to compile that wrong code with Clang 3.8, I get:
error: 'refRefVal' declared as a reference to a reference
Other compilers give similar errors. For example, Microsoft Visual C++ gives:
error C2529: 'refRefVal': reference to reference is illegal
When you use a reference, the effect is to use the object it refers to.
References are dereferenced automatically in most contexts where they appear. Anything you try to do to a reference, really you are doing it to the object it refers to. Unlike pointers, there is no operator for dereferencing a reference; in effect the reference is a name for the referenced object.
What you have written (int &refVal3 = refVal;) is not an error, because you are simply initializing a second reference bound to the same object. To see why this is, consider the effect of several of your statements.
Here you create an int, initializing it with the value 1024:
int ival = 1024;
Here you make an lvalue reference, bound to that int object:
int &refVal = ival;
Here you assign 2 to the original int object, because refVal is used as the object to which it refers:
refVal = 2;
Here you create a second int object, initialized with the value of the original object, also because refVal is used as the object to which it refers:
int ii = refVal;
Here you make a second lvalue reference to the original object, also because refVal is used as the object to which it refers:
int &refVal3 = refVal;
Code that looks like it creates a second reference to the first one is, therefore, really creating a second reference to the original object.
This is to say that the reason int &refVal3 = refVal; introduces another reference to the original object--rather than attempting to create a reference to a reference--is that this is just another consequence of refVal being automatically taken to mean the int it refers to.
Reference Collapsing
You can't write types named like T & & yourself, but what about this?
using Ref = int&;
using RefRef = Ref&; // I named this poorly, it's not really a reference to a reference!
This causes the compiler to see that I am trying to make a type alias RefRef to be int& &. The compiler follows the rules of reference collapsing. It collapses the two references into one, so the effect is the same as if I had written:
using RefRef = int&;
This behavior is useful in situations that involve type deduction, such as with templates, both by allowing more code to compile and work as expected than otherwise would, and by facilitating perfect forwarding. (One might argue it also parallels what you observed--when you initialize references from references, you can still never get a reference to a reference, only to an object.)
In no case is there ever anything whose type is reference to reference. The C++ language simply does not have any such types.
I am currently learning C++ from C++ Primer, and it explains how a reference is an alias to another variable name. It also explains how a pointer points to another variable. It states that the difference between a pointer and a reference is that pointers can be reassigned and references can't.
In the following code example, what can I do with the pointer or reference that I can't do with the other?
double pi = 3.14;
double &piRef = pi;
double *const piPnt = π
//both of these examples are valid and do the same thing
piRef = 3.14159;
*piPnt = 3.14159;
//however, if I attempt to reassign what the pointer points to, it is illegal.
//this is the same as with a reference, as a reference can't be reassigned either
double tau = 6.28;
piPnt = τ
I am aware of the internal differences of each (such as that a pointer is an object, a reference isn't). I am interested in how those differences matter to the programmer beyond a slightly different syntax. As such, this is not a duplicate of this question in which the accepted answer only talks about internal differences.
From a functional point of view pointers and references are indeed the same thing... they reference an object and are not a copy of that object.
The only real difference in addition to not being able to rebind a reference is that a pointer can be NULL (i.e. it can point to nothing) while a reference is assumed to always reference an object.
You technically can actually end up with a reference that is referencing no object (e.g. passing *p to a function expecting a reference where p is the null pointer) but this is "undefined behavior".
In other words pointers are more "flexible" than references and this allows the compiler to ignore
That a reference can change the object it's referencing
That a reference can have no object
And this can in some cases produce faster code (however for the second point de-referencing a null pointer is undefined behavior and not a runtime error; the compiler therefore is not mandated to generate code that does something special in this case: that a pointer can actually point to no object is indeed irrelevant from a code generation point of view because it's in the contract between programmer and compiler that this will never happen).
The "price" to pay for the added flexibility of rebinding and having NULLs is that the syntax is (somewhat gratuitously) more annoying.
If the pointer cannot be reassigned (because the pointer itself is const) then there are no practical differences whatsoever except for the more verbose-but-explicit syntax. This because despite being an object a const-declared pointer cannot be altered even using aliasing.
While from a syntactic point of view you can take the address of the const pointer, cast the address to an address of a non-const pointer and change the value pointed to, such an operation would be undefined behavior and whatever happens (e.g. ignoring the assignment) the compiler is going to be right if taken to court :-)
what can I do with the pointer or reference that I can't do with the other?
References allow you to write certain constructors and overload operators:
class X
{
// copy constructor
X(const X& a);
// move constructor
X(X&& a);
// copy assignment operator
X& operator=(const X& a);
// move assignment operator
X& operator=(X&& a);
}
(Indeed, operator overloading was the motivating use case for introducing references into C++.)
What is often overlooked is the fact that modern C++ distinguishes between X& (a reference to an lvalue) and X&& (a reference to an rvalue), but there is no such thing as a pointer to an rvalue.
what can I do with the pointer or reference that I can't do with the
other?
double *const piPnt = π
The above statement
marks piPnt as a read only variable in memory layout
So, piPnt = &xyz would throw an error now.
But changing the value at the address the pointer points to is still valid.
That is , *piPnt = 56 is fine.
Const Pointers are useful in embedded systems that need to refer to the same memory (port mapping). It's a one time mapping and constant pointers are helpful here.
Now with regards to references:
double &piRef = pi;
You cannot reinitialize a reference in C++. You can assign different value to the object it refers to. This is one and the same object for that reference forever. And this is what you did in your example.
piRef = 3.14159;
A reference cannot be changed to refer to another object after
initialization. Note that initialization of a reference is treated
very differently from assignment to it. Argument passing (5.2.2) and
function value return (6.6.3) are initializations.
Some places where references are useful:
Pointers cannot point to temporaries, the standard expressly forbids doing it. References can bind to temporaries.
A pointer can be NULL while a reference is assumed to always reference an object. You can still return null from a function returning a reference, the compiler would not complain about it, but that is suicidal.
I often see statements like below in C++ books regarding reference:
Reference is just another name of the original object. When it is used, it is replaced by the original object (in most cases).
Here is the question:
If I bind a const ref to a non-const object, when this const ref being used and replaced by the original object, does the const-ness goes away?
int i = 42;
const int & r1 = i;
int & r2 = r1; // Question: shouldn't r1 here just be replaced by the original object, which is **non-const**?
Simple answer: No
Longer answer: You cannot do that. Once you have a const & it will always stay const (unless you do const casting or some other explicit things). This is by design as otherwise const-correctness wouldn't really mean much and also the reason why you cannot compile your code.
The reason why int & r2 = r1; fails is because you dropped cv-qualifier const when reference-relate r2 to r1.
See C++ standard working draft, n3797, 8.5.3/5:
If T1 is reference-related to T2, cv1 shall be the same cv-qualification as, or greater
cv-qualification than, cv2.
First of all, the provided code is not valid, since you cannot initialize non-constant reference with constant reference.
Regarding your question, it might be easier to consider reference as a special kind of pointer to the object, which is automatically referenced (the sentence you quoted is quite misleading).
Your question is confused, anyway, assigning to a reference does not change the constness of the original object.
And your code won't compile without const_cast.
int& r2 = const_cast<int&>(r1);
You can use const_cast for casting away the constness.
The books are lying. C++ references are * const pointers in disguise with some special syntax. A const reference can extend the life of a temporary, which pointers can't. That's it.
References were only invented to allow convenient syntax for operator overloading. I still like references and use them, but I'm under no illusions that they are "magical" and "totally different to pointers".
It is not possible to assign an integer value to a reference variable directly, say like:
int &x=10; //not possible
Is there any other way we can modify this statement to make it possible?
But not like this:
int a=10;int &x=a;
This works fine. But I want some other way or modify a little bit my expression and make it work!
The reference as the name says has to reference to something. How do you want to assign a value to it if it doesn't reference anything?
The reason it doesn't work is because 10 is of the type "const int". You can turn that into a reference, but you can't make it non-const without violating some logic at the least.
const int &a = 10;
that'll work.
int &b = const_cast<int &>(static_cast<const int &>(10));
will also compile, but you can't modify b (as that would imply modifying the actual "10" value).
The crux is that 10 is a constant – somewhat obviously: you cannot change its value. But if you try to assign it to an int reference, this would mean that the value were modifiable: an int& is a modifiable value.
To make your statement work, you can use a const reference:
int const& x = 10;
But as “cnicutar” has mentioned in a comment, this is pretty useless; just assign the 10 to a plain int.
You can't bind a reference-to-nonconst to anything immutable.
The standard permits storing compile time constants in ROM (btw, attempting to modify const_cast<>ed compile time constants yields undefined behaviour)
This would basically strip of the const, even if the const is invisible, therefore subverting the whole const-correctness-thing
However, you can bind a reference-to-const to nearly everything, including temporaries:
GotW: A candidate for the most important const
Consider this a "feature".
References refer to objects (perhaps temporary objects), not to values. If you want to store a value somewhere, assign it to an object, not to a reference.
As a special case, const int &a = 10; initializes the reference a to refer to a temporary object with the value 10, and it extends the lifetime of that temporary to the end of the scope of a (12.2/5). That's pretty useless with an integer literal, but occasionally useful with objects of class type. Still, this does not assign an integer value to a reference. It creates a temporary, and binds a reference to the temporary.
in the C++0x, you can use int&& (rvalue references ), but this can be used as function parameter.