Using ternary operator to initialize a reference variable? - c++

Putting all the maintainability and reading issues aside, can these lines of code generate undefined behavior?
float a = 0, b = 0;
float& x = some_condition()? a : b;
x = 5;
cout << a << ", " << b;

No, it's just fine. It would not create undefined behavior in this code. You will just change value of a or b to 5, according to condition.

This is absolutely fine, as long as both sides of the conditional are expressions that can be used to initialize a reference (e.g. variables, pointer dereferences, etc)
float& x = some_condition()? a : *(&b); // This is OK - it is the same as your code
float& x = some_condition()? a : b+1; // This will not compile, because you cannot take reference of b+1

Related

Why am I not getting an error when reseting a reference?

In the following code
#include<iostream>
using namespace std;
int main(){
int x=4;
int y=5;
cout << x <<endl;
int& foo = x;
// foo is now a reference to x so this sets x to 56
foo = 56; //reseting the reference foo
cout << x <<endl;
cout << foo <<endl;
foo= y; //this is supposed to be forbidden
cout << foo <<endl; // but I am getting foo=5
return 1;
}
This is compiling fine and I get foo=5 in the last cout. For what I've read you cannot reseat a reference to make it refer to a different object, so I was expecting an error, what is going here?
Once a reference has been created, any attempt to use the reference variable is equivalent to using the object that was assigned to it.
So when we look at the following line of code:
foo= y; //this is suppose to be forbidden
We see that it does not attempt to re-assign the reference, it assigns the value of y to the object referred to by foo.
What would be an example of reseting a reference (that would generate an error), then?
While we can't write code that reassigns references, the compiler can end up in situations where it would have to emit code that is the equivalent of that when dealing with references embedded within other data structures. And it's still illegal for the compiler to do so.
For example, the following will fail to compile because the compiler-generated assignement operator for X cannot be created since it would involve reassigning ref.
struct X {
int& ref;
};
void foo() {
int v1 = 1;
int v2 = 2;
X x1{v1};
X x2{v2};
x1 = x2; // Boom!
}
Edit: Answering the followup question here because it's extremely relevant.
Now you may wonder: "Hey! if the reference behaves as a proxy for another variable, why can't the compiler just use that logic to generate a valid assignment operator for struct X?"
Well, not really. The underlying expectation here is that once i do var_a = var_b, then by default, var_a is now effectively the same as var_b.
So if x1.ref points to v1 and x2.ref points to v2, then the expectation is that by default, x1 = x2 would lead to x1.ref pointing to v2.
Doing anything else would just lead to confusion and surprises in the long run.

C++ Why can const X& be modified by function?

From another post, I test the example regarding const X& x in function. This link What does “const X& x” mean? says "It means x aliases an X object, but you can’t change that X object via x." But below example shows we can modify X object? Why?
#include <iostream>
using namespace std;
int a = 1;
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
cout << "n: " << n << endl;
//f(); // Might change the value of whatever n refers to
}
int main()
{
DoWork(a);
cout << "a: " << a << endl;
}
Follow up question:
For example, we have a function return const reference, why can it be assigned to non-const reference?
const X& tmp = get();
X& other = tmp; //this works, but why? this get rids of const X& in tmp?
I think you have confusion about const and references.
Firstly what is reference `
it's another name(alias) for an object.
About what's written in function parameters means that n is a const reference. It means you can't change a through n.(or in the simple sense, n can't be on the left side of the assignment operator. It also means that n is bounded to the object permanently.
n = n+2; // This is an error as n is const and hence you can't use n to modify the object it is pointing.
a = n+2; // here you are not using `n` to modify. So this is ok.
int x = 10 ;
n = x; // error as n is bounded to `a` so it can't be bounded again to any other variable x. You can see `n` is on left which is an error.
Now if you do this
cout<<a<<"\t"<<n<<endl; // Output 3; (1+2)
About follow up question. (Read it carefully)
Let us suppose a general scenario.
const int ci = 1024;
int &r2 = ci; // error: non const reference to a const object
The same is the case with your function question. You must be wondering why we can't do such a thing.
See here c1 is const so we can't change it. Now suppose(JUST SUPPOSE) the following code would have been a legal code. Then r2 will be a reference to c1.
Now, As i stated reference is other name of an object. It means if we do arithmetic on r2 it will be done on ci.
r = r+2; // it means ci = ci + 2 ;
but see ci was const(it shouldn't be altered but we altered it.)
So we need to do something like.
const int ci = 1024;
const int &r2 = ci; // error: non-const reference to a const object
This ensures that we can't do
r = r +2; which ensures/preserves the constness of ci.
get() is returning const so to preserve it's constness the variable used to receive from function must be const. So, the part which you said is working is an error as per standard, may be it's your compiler extension.
Keep in mind that const X& essentially means that the compiler imposes restriction that you cannot mutate the value reference via the reference (as the page you linked says). The actual value referenced by the reference is purely determined at runtime and the compiler cannot infer that you are mutating the value referenced by x when it sees the line a = n * 2;.
A small change to your code can help what is going on,
#include <iostream>
using namespace std;
int a = 1;
int b = 1; // new variable
void DoWork(const int &n)
{
a = n * 2; // If n was a reference to a, n will have been doubled
cout << "n: " << n << endl;
//f(); // Might change the value of whatever n refers to
}
int main()
{
// Whether `x` in `DoWork` refers to `b` or `a` is purely determined at runtime.
if (rand() % 2 == 0)
DoWork(a);
else
DoWork(b);
cout << "a: " << a << endl;
}
In other words, the compiler cannot be sure of what values references will point to. In C++, references can even point to invalid memory,
int *a = 0;
int &ref = *a;
cout << ref; // runtime error
Hope this helps.
Edit:
Regarding the followup question, what compiler are you using? Any standard compiler would refuse to compile the code.

const_cast<double*> works but const_cast<int*> doesn't

My problem is, why first part of the code does not work while the second one works. Non-const pointer should modify the const value using the const_cast previously but with integers this trick does not work. Could you explain why that happens?
const int i = 5;
cout << i << endl; //output: 5
int *ptr_i = const_cast<int*>(&i);
*ptr_i = 100;
cout << i << endl; //output : 5
const double d = 5;
cout << d << endl; //output : 5
double *ptr_d = const_cast<double*>(&d);
*ptr_d = 100.;
cout << d << endl; //output : 100
Modifying a const variable is undefined behaviour:
n4296 §7.1.6.1/4
Except that any class member declared mutable (7.1.1) can be modified,
any attempt to modify a const object during its lifetime (3.8) results
in undefined behavior.
const_cast is generally for communicating with non-const-correct APIs or casting away volatile qualifiers; it shouldn't be used like this.
Non-const pointer should modify the const value using the const_cast previously but with integers this trick does not work.
No, non-const pointer modifying the const value is undefined behavior. It is not supposed to work.
Could you explain why that happens?
Since this is UB, the compiler is free to do anything at all here, and "anything at all" means the code will happen to work in the case of int only (at least, for your compiler).
TLDR: undefined behavior is undefined.

Function parameters evaluation order: is it UB if we pass reference?

This is undefined behavior:
void feedMeValue(int x, int a) {
cout << x << " " << a << endl;
}
int main() {
int a = 2;
int &ra = a;
feedMeValue(ra = 3, a); // equivalent to: feedMeValue(a = 3, a) (see note bellow)
return 0;
}
because depending on what parameter gets evaluated first we could call (3, 2) or (3, 3).
However this:
void feedMeReference(int x, int const &ref) {
cout << x << " " << ref << endl;
}
int main() {
int a = 2;
int &ra = a;
feedMeReference(ra = 3, a); // equivalent to: feedMeReference(a = 3, a) (see note bellow)
return 0;
}
will always output 3 3 since the second parameter is a reference and all parameters have been evaluated before the function call, so even if the second parameter is evaluated before of after ra = 3, the function received a reference to a which will have a value of 2 or 3 at the time of the evaluation, but will always have the value 3 at the time of the function call.
Is the second example UB? It is important to know because the compiler is free to do anything if it detects undefined behavior, even if I know it would always yield the same results.
Note: I will leave feedMeReference(ra = 3, a) as some answers reference ra but you should note that a simpler equivalent problem to this is if we call feedMeReference(a = 3, a) (simpler because we eliminate ra which is just in the way of our issue (the second parameter being a reference)).
That's an interesting question. In your first case, there is
undefined behavior because an object is modified and also
accessed without an intervening sequence point (in the language
of C++03---C++11 used different language to say essentially the
same thing). In the second, there is no undefined behavior,
because initializing a reference with an lvalue doesn't access
the object, so the only access is ra = 3. (Calling the
function establishes a sequence point, so accesses in the
function have a sequence point between them and the ra = 3.)

Learning about lvalue and rvalue and not sure why this code works

I'm was playing around with C++(It's been over 10 years since I used the language) and wrote the the small program below and was a bit surprised by the result:
#include <iostream>
int& getReference()
{
int x = 33;
return x;
}
int main()
{
auto a = getReference() = 4;
auto b = getReference();
std::cout << "a: " << a << " b: " << b;
return 0;
}
The output is "a: 4 c: 33" when I was expecting "a: 33 c: 33".
The reason I was expecting "a" to equal 33 is because i'm first setting the getReference lvalue return reference to 4 and than "return x" is overriding that lvalue reference with 33.
Anyway, I thought maybe can explain where I'm wrong here?
Thanks for everyone's help!
Edit 1
So many responses really quick. Thanks everyone for your help!
Edit 2 I'm not sure if it's too late to add this or I should make it a new question. I changed "x" as a global variable and got the same answer. So i'm now still confused because the global variable x didn't go away.
Your getReference returns a reference to a local variable, so by the time the function returns, it's a dangling reference (i.e., the variable to which it referred has been destroyed). Any use of that reference (reading or writing) causes undefined behavior.
So, basically, you've broken the contract with the compiler in a way that allows it to do anything. You have no right to be surprised at any result.
If you change it to a global:
#include <iostream>
int x;
int& getReference()
{
x = 33;
return x;
}
int main()
{
auto a = getReference() = 4;
auto b = getReference();
std::cout << "a: " << a << " b: " << b;
return 0;
}
Then you should expect a: 4 b: 33. The first invocation retrieves a reference to x, then assigns 4 to the referent, than retrieves 4 from the referent and assigns it to a. The second invocation assigns 33 to the referent, then retrieves the 33 from the referent and assigns it to b.
If you changed a and b to references:
// ...
auto &a = getReference() = 4;
auto &b = getReference();
// remainder unchanged
Then you should see a:33 b:33. In this case, both a and b refer to the original variable. Since getReference assigns 33 after the 4 gets assigned, the value 4 is overwritten with 33.
In other words, in this case both a and b are just references to the single global, so the overall effect is pretty much the same as if you had:
int x;
int main(){
x = 33;
x = 4;
x = 33;
std::cout << x << " " << x << '\n';
}
A function should not return a reference to a local variable. The problem is that you're returning a reference to x, but x goes out of scope at the end of the function. As soon as it goes out of scope, any references to it are invalid and dereferencing them invokes undefined behavior.
If that were fixed, and you returned a reference to a globally-scoped variable instead, you would still see the same behavior, because:
auto a = getReference() = 4;
The right-hand expression is evaluated first:
getReference() = 4
This assigns 4 to the reference returned by the function. Then, the result of that expression (4) is assigned to a.
auto b = getReference();
Here, getReference() is evaluated. As you pointed out, the function resets the value of the variable it returns a reference to. So the result of the expression is 33 which gets assigned to b.
That code is undefined behavior, you are returning a reference to a local object. Any use of the reference will be undefined behavior. Now, assuming that the reference returned was to an object with a longer life span...
auto a = getReference() = 4;
The order of operations is equivalent to the parenthesized expresssion:
auto a = (getReference() = 4);
That is, you get a reference to an object (hopefully alive!) update that object to hold the value 4, then assign the result of that to the newly created a.