Can I initialize a constant integer with a L-value? - c++

I've got a question in my assignment that asks me to evaluate whether the following function call is correct. I'm not sure if a const int can be initialized with a variable of type const int&. I know that a const int can be initialized with another int, for example
int i=3; const int j=i
works perfectly fine, but I'm not sure if the following code is semantically correct (the line const int j=bar(++i);)
int foo (int& i) {return i+=2;}
const int& bar(int &i){ return i+=2;}
int main(){
int i=5;
const int j=bar(++i);
}

Yes it can. You can initialize an object with any value category.
When constructing the type, it will simply call its constructor with the correct overload, such as type(type&& other) or type(type const&).
For trivial types, it's always a copy. So as long as the types are compatible, no matter their value category, it will work.
Initializing a reference is different. You must have an expression with a compatible value category. For example, creating a mutable reference from your function won't work:
int& j = bar(++i); // won't compile, int& cannot be bound to int const&
This is because bar returns a reference to const, thus cannot be bound to a reference to mutable.
As a side note, even though it's an int constant, it is not a compile time constant anymore. Thus you won't be able to use it as an array size or template parameter.
To fix that you'd have to use constexpr, which will guarantee that the value of your variable is available at compile time.
int a = 9;
constexpr int b = a; // Won't work, `a` is a runtime value, `b` is compile time
constexpr int a = 1;
constexpr int b = a + 1; // Works! Both compile time values

A reference is basically just an alias to the object it is bound to (with possible indication of immutability by const). Since bar(++i) returns a reference bound to i, it is the same as if you initialized j by i after bar(++i) call:
bar(++i);
const int j = i;
Const reference just says that you cannot modify the bound object through that reference. But there is nothing in your code that would try doing this.

Related

Where and why do we use a reference to const? [duplicate]

void DoWork(int n);
void DoWork(const int &n);
What's the difference?
The important difference is that when passing by const reference, no new object is created. In the function body, the parameter is effectively an alias for the object passed in.
Because the reference is a const reference the function body cannot directly change the value of that object. This has a similar property to passing by value where the function body also cannot change the value of the object that was passed in, in this case because the parameter is a copy.
There are crucial differences. If the parameter is a const reference, but the object passed it was not in fact const then the value of the object may be changed during the function call itself.
E.g.
int a;
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
f(); // Might change the value of whatever n refers to
}
int main()
{
DoWork(a);
}
Also if the object passed in was not actually const then the function could (even if it is ill advised) change its value with a cast.
e.g.
void DoWork(const int &n)
{
const_cast<int&>(n) = 22;
}
This would cause undefined behaviour if the object passed in was actually const.
When the parameter is passed by const reference, extra costs include dereferencing, worse object locality, fewer opportunities for compile optimizing.
When the parameter is passed by value an extra cost is the need to create a parameter copy. Typically this is only of concern when the object type is large.
The difference is more prominent when you are passing a big struct/class:
struct MyData {
int a,b,c,d,e,f,g,h;
long array[1234];
};
void DoWork(MyData md);
void DoWork(const MyData& md);
When you use use 'normal' parameter, you pass the parameter by value and hence creating a copy of the parameter you pass. If you are using const reference, you pass it by reference and the original data is not copied.
In both cases, the original data cannot be modified from inside the function.
EDIT:
In certain cases, the original data might be able to get modified as pointed out by Charles Bailey in his answer.
There are three methods you can pass values in the function
Pass by value
void f(int n){
n = n + 10;
}
int main(){
int x = 3;
f(x);
cout << x << endl;
}
Output: 3. Disadvantage: When parameter x pass through f function then compiler creates a copy in memory in of x. So wastage of memory.
Pass by reference
void f(int& n){
n = n + 10;
}
int main(){
int x = 3;
f(x);
cout << x << endl;
}
Output: 13. It eliminate pass by value disadvantage, but if programmer do not want to change the value then use constant reference
Constant reference
void f(const int& n){
n = n + 10; // Error: assignment of read-only reference ‘n’
}
int main(){
int x = 3;
f(x);
cout << x << endl;
}
Output: Throw error at n = n + 10 because when we pass const reference parameter argument then it is read-only parameter, you cannot change value of n.
With
void DoWork(int n);
n is a copy of the value of the actual parameter, and it is legal to change the value of n within the function. With
void DoWork(const int &n);
n is a reference to the actual parameter, and it is not legal to change its value.
Since none of you mentioned nothing about the const keyword...
The const keyword modifies the type of a type declaration or the type of a function parameter, preventing the value from varying. (Source: MS)
In other words: passing a parameter by reference exposes it to modification by the callee. Using the const keyword prevents the modification.
The first method passes n by value, i.e. a copy of n is sent to the function. The second one passes n by reference which basically means that a pointer to the n with which the function is called is sent to the function.
For integral types like int it doesn't make much sense to pass as a const reference since the size of the reference is usually the same as the size of the reference (the pointer). In the cases where making a copy is expensive it's usually best to pass by const reference.
Firstly, there is no concept of cv-qualified references. So the terminology 'const reference' is not correct and is usually used to describle 'reference to const'. It is better to start talking about what is meant.
$8.3.2/1- "Cv-qualified references are ill-formed except when the
cv-qualifiers are introduced through the use of a typedef (7.1.3) or
of a template type argument (14.3), in which case the cv-qualifiers
are ignored."
Here are the differences
$13.1 - "Only the const and volatile type-specifiers at the outermost
level of the parameter type specification are ignored in this fashion;
const and volatile type-specifiers buried within a parameter type
specification are significant and can be used to distinguish
overloaded function declarations.112). In particular, for any type T,
“pointer to T,” “pointer to const T,” and “pointer to volatile T” are
considered distinct parameter types, as are “reference to T,”
“reference to const T,” and “reference to volatile T.”
void f(int &n){
cout << 1;
n++;
}
void f(int const &n){
cout << 2;
//n++; // Error!, Non modifiable lvalue
}
int main(){
int x = 2;
f(x); // Calls overload 1, after the call x is 3
f(2); // Calls overload 2
f(2.2); // Calls overload 2, a temporary of double is created $8.5/3
}
Also, you can use the const int& x to initialize it with r-value and this will cause that you can't change x or bind it with another values.
const int& x = 5; // x is a constant reference to r-value 5
x = 7; // expression is not a modifable value

Passing const value by reference

Let's say I have this program:
const int width = 4;
void test(int&){}
int main() {
test(width);
}
This will fail to compile. I notice that constant values ( also enumeration constants ) with names ( such as width ) cannot be passed by reference. Why is that so?
Imagine this:
void test (int& j) { j++; }
If test does change the value of the thing referenced, clearly we can't call it with a const parameter. And if it doesn't, why does it take its parameter by non-const reference?
Passing by reference allows us to change the actual object.
If an object is defined as const, it cannot be changed. That is exactly what const means - it's constant.

c++ function: pass non const argument to const reference parameter

suppose I have a function which accept const reference argument pass,
int func(const int &i)
{
/* */
}
int main()
{
int j = 1;
func(j); // pass non const argument to const reference
j=2; // reassign j
}
this code works fine.according to C++ primer, what this argument passing to this function is like follows,
int j=1;
const int &i = j;
in which i is a synonym(alias) of j,
my question is: if i is a synonym of j, and i is defined as const, is the code:
const int &i = j
redelcare a non const variable to const variable? why this expression is legal in c++?
The reference is const, not the object. It doesn't change the fact that the object is mutable, but you have one name for the object (j) through which you can modify it, and another name (i) through which you can't.
In the case of the const reference parameter, this means that main can modify the object (since it uses its name for it, j), whereas func can't modify the object so long as it only uses its name for it, i. func could in principle modify the object by creating yet another reference or pointer to it with a const_cast, but don't.
const int &i = j;
This declares a reference to a constant integer.
Using this reference, you won't be able to change the value of the integer that it references.
You can still change the value by using the original variable name j, just not using the constant reference i.

What is a constant reference? (not a reference to a constant)

Why do constant references not behave the same way as constant pointers, so that I can actually change the object they are pointing to? They really seem like another plain variable declaration. Why would I ever use them?
This is a short example that I run which compiles and runs with no errors:
int main (){
int i=0;
int y=1;
int&const icr=i;
icr=y; // Can change the object it is pointing to so it's not like a const pointer...
icr=99; // Can assign another value but the value is not assigned to y...
int x=9;
icr=x;
cout<<"icr: "<<icr<<", y:"<<y<<endl;
}
The clearest answer.
Does “X& const x” make any sense?
No, it is nonsense
To find out what the above declaration means, read it right-to-left:
“x is a const reference to a X”. But that is redundant — references
are always const, in the sense that you can never reseat a reference
to make it refer to a different object. Never. With or without the
const.
In other words, “X& const x” is functionally equivalent to “X& x”.
Since you’re gaining nothing by adding the const after the &, you
shouldn’t add it: it will confuse people — the const will make some
people think that the X is const, as if you had said “const X& x”.
The statement icr=y; does not make the reference refer to y; it assigns the value of y to the variable that icr refers to, i.
References are inherently const, that is you can't change what they refer to. There are 'const references' which are really 'references to const', that is you can't change the value of the object they refer to. They are declared const int& or int const& rather than int& const though.
What is a constant reference (not a reference to a constant)
A Constant Reference is actually a Reference to a Constant.
A constant reference/ Reference to a constant is denoted by:
int const &i = j; //or Alternatively
const int &i = j;
i = 1; //Compilation Error
It basically means, you cannot modify the value of type object to which the Reference Refers.
For Example:
Trying to modify value(assign 1) of variable j through const reference, i will results in error:
assignment of read-only reference ‘i’
icr=y; // Can change the object it is pointing to so it's not like a const pointer...
icr=99;
Doesn't change the reference, it assigns the value of the type to which the reference refers.
References cannot be made to refer any other variable than the one they are bound to at Initialization.
First statement assigns the value y to i
Second statement assigns the value 99 to i
This code is ill-formed:
int&const icr=i;
Reference: C++17 [dcl.ref]/1:
Cv-qualified references are ill-formed except when the cv-qualifiers are introduced
through the use of a typedef-name or decltype-specifier, in which case the cv-qualifiers are ignored.
This rule has been present in all standardized versions of C++. Because the code is ill-formed:
you should not use it, and
there is no associated behaviour.
The compiler should reject the program; and if it doesn't, the executable's behaviour is completely undefined.
NB: Not sure how none of the other answers mentioned this yet... nobody's got access to a compiler?
By "constant reference" I am guessing you really mean "reference to constant data". Pointers on the other hand, can be a constant pointer (the pointer itself is constant, not the data it points to), a pointer to constant data, or both.
As it mentioned in another answers, a reference is inherently const.
int &ref = obj;
Once you initialized a reference with an object, you can't unbound this reference with its object it refers to. A reference works just like an alias.
When you declare a const reference, it is nothing but a reference which refers to a const object.
const int &ref = obj;
The declarative sentences above like const and int is determining the available features of the object which will be referenced by the reference. To be more clear, I want to show you the pointer equivalent of a const reference;
const int *const ptr = &obj;
So the above line of code is equivalent to a const reference in its working way. Additionally, there is a one last point which I want to mention;
A reference must be initialized only with an object
So when you do this, you are going to get an error;
int &r = 0; // Error: a nonconst reference cannot be initialized to a literal
This rule has one exception. If the reference is declared as const, then you can initialize it with literals as well;
const int &r = 0; // a valid approach
First I think int&const icr=i; is just int& icr = i, Modifier 'const' makes no sense(It just means you cannot make the reference refer to other variable).
const int x = 10;
// int& const y = x; // Compiler error here
Second, constant reference just means you cannot change the value of variable through reference.
const int x = 10;
const int& y = x;
//y = 20; // Compiler error here
Third, Constant references can bind right-value. Compiler will create a temp variable to bind the reference.
float x = 10;
const int& y = x;
const int& z = y + 10;
cout << (long long)&x << endl; //print 348791766212
cout << (long long)&y << endl; //print 348791766276
cout << (long long)&z << endl; //print 348791766340

What is the difference between a const reference and normal parameter?

void DoWork(int n);
void DoWork(const int &n);
What's the difference?
The important difference is that when passing by const reference, no new object is created. In the function body, the parameter is effectively an alias for the object passed in.
Because the reference is a const reference the function body cannot directly change the value of that object. This has a similar property to passing by value where the function body also cannot change the value of the object that was passed in, in this case because the parameter is a copy.
There are crucial differences. If the parameter is a const reference, but the object passed it was not in fact const then the value of the object may be changed during the function call itself.
E.g.
int a;
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
f(); // Might change the value of whatever n refers to
}
int main()
{
DoWork(a);
}
Also if the object passed in was not actually const then the function could (even if it is ill advised) change its value with a cast.
e.g.
void DoWork(const int &n)
{
const_cast<int&>(n) = 22;
}
This would cause undefined behaviour if the object passed in was actually const.
When the parameter is passed by const reference, extra costs include dereferencing, worse object locality, fewer opportunities for compile optimizing.
When the parameter is passed by value an extra cost is the need to create a parameter copy. Typically this is only of concern when the object type is large.
The difference is more prominent when you are passing a big struct/class:
struct MyData {
int a,b,c,d,e,f,g,h;
long array[1234];
};
void DoWork(MyData md);
void DoWork(const MyData& md);
When you use use 'normal' parameter, you pass the parameter by value and hence creating a copy of the parameter you pass. If you are using const reference, you pass it by reference and the original data is not copied.
In both cases, the original data cannot be modified from inside the function.
EDIT:
In certain cases, the original data might be able to get modified as pointed out by Charles Bailey in his answer.
There are three methods you can pass values in the function
Pass by value
void f(int n){
n = n + 10;
}
int main(){
int x = 3;
f(x);
cout << x << endl;
}
Output: 3. Disadvantage: When parameter x pass through f function then compiler creates a copy in memory in of x. So wastage of memory.
Pass by reference
void f(int& n){
n = n + 10;
}
int main(){
int x = 3;
f(x);
cout << x << endl;
}
Output: 13. It eliminate pass by value disadvantage, but if programmer do not want to change the value then use constant reference
Constant reference
void f(const int& n){
n = n + 10; // Error: assignment of read-only reference ‘n’
}
int main(){
int x = 3;
f(x);
cout << x << endl;
}
Output: Throw error at n = n + 10 because when we pass const reference parameter argument then it is read-only parameter, you cannot change value of n.
With
void DoWork(int n);
n is a copy of the value of the actual parameter, and it is legal to change the value of n within the function. With
void DoWork(const int &n);
n is a reference to the actual parameter, and it is not legal to change its value.
Since none of you mentioned nothing about the const keyword...
The const keyword modifies the type of a type declaration or the type of a function parameter, preventing the value from varying. (Source: MS)
In other words: passing a parameter by reference exposes it to modification by the callee. Using the const keyword prevents the modification.
The first method passes n by value, i.e. a copy of n is sent to the function. The second one passes n by reference which basically means that a pointer to the n with which the function is called is sent to the function.
For integral types like int it doesn't make much sense to pass as a const reference since the size of the reference is usually the same as the size of the reference (the pointer). In the cases where making a copy is expensive it's usually best to pass by const reference.
Firstly, there is no concept of cv-qualified references. So the terminology 'const reference' is not correct and is usually used to describle 'reference to const'. It is better to start talking about what is meant.
$8.3.2/1- "Cv-qualified references are ill-formed except when the
cv-qualifiers are introduced through the use of a typedef (7.1.3) or
of a template type argument (14.3), in which case the cv-qualifiers
are ignored."
Here are the differences
$13.1 - "Only the const and volatile type-specifiers at the outermost
level of the parameter type specification are ignored in this fashion;
const and volatile type-specifiers buried within a parameter type
specification are significant and can be used to distinguish
overloaded function declarations.112). In particular, for any type T,
“pointer to T,” “pointer to const T,” and “pointer to volatile T” are
considered distinct parameter types, as are “reference to T,”
“reference to const T,” and “reference to volatile T.”
void f(int &n){
cout << 1;
n++;
}
void f(int const &n){
cout << 2;
//n++; // Error!, Non modifiable lvalue
}
int main(){
int x = 2;
f(x); // Calls overload 1, after the call x is 3
f(2); // Calls overload 2
f(2.2); // Calls overload 2, a temporary of double is created $8.5/3
}
Also, you can use the const int& x to initialize it with r-value and this will cause that you can't change x or bind it with another values.
const int& x = 5; // x is a constant reference to r-value 5
x = 7; // expression is not a modifable value