Error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’ - c++

I need to create a Bar object, which has a private object Foo f.
However, the value of Foo object parameter should be passed by the specific method int genValue().
If I initialize f in the constructor scope Bar(){...}, the compiler yell error, something like there is no constructor Foo().
If I construct like this Bar(): f(genValue()), the compiler yells the error:
test.cpp: In constructor ‘Bar::Bar()’:
test.cpp:16:19: error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
Bar(): f(genValue()){
~~~~~~~~^~
test.cpp:7:2: note: initializing argument 1 of ‘Foo::Foo(int&)’
Foo(int &x) {
^~~
class Foo {
public:
Foo(int &x) {
this->x = x;
}
private:
int x;
};
class Bar {
public:
Bar(): f(genValue()){
}
private:
Foo f;
int genValue(){
int x;
// do something ...
x = 1;
return x;
}
};
int main() {
Bar bar ();
return 0;
}
How can I fix the problem, if I don't want to modify Foo class and its argument value should be passed from genValue()? And, I don't want to use pure pointer (*), but a solution with smart pointer is okay!

Your Foo type is garbage as written. This causes your error.
Foo(int &x) {
this->x = x;
}
there is (a) absolutely no reason to take x by reference here, and (b) even less reason to take it by non-const reference.
Any of the following fix both Foo and your error.
Foo(int const&x) {
this->x = x;
}
Foo(int const&x_in):x(x_in) {
}
Foo(int x) {
this->x = x;
}
Foo(int x_in):x(x_in) {
}
and, if the value isn't actually an int yet is cheap-to-move:
Foo(int x) {
this->x = std::move(x);
}
Foo(int x_in):x(std::move(x_in)) {
}
these are 6 independent solutions to your problem.
For an int I would use #4; for a non-int #6.
Fixing this outside of Foo is a bad idea, because you are getting the error because Foo was written wrong. The rest of your code is fine, avoid breaking good code.

Don't pass int&, it can't be bound to a constant or temporary because those can't be modified - use const int& instead.
Actually for simple types you should prefer to pass by value instead, and let the optimizer worry about providing the best implementation.

A non-const reference parameter, such as an int&, can only refer to an "lvalue," which is a named variable.
auto takes_nonconst_reference = [](int&){};
auto takes_const_reference = [](const int&){};
auto takes_value = [](int){};
auto returns_int = []{return 42;};
int foo = 1;
// OK
takes_nonconst_reference(foo);
takes_const_reference(foo);
takes_const_reference(returns_int());
takes_value(foo);
takes_value(returns_int());
// compilation error, value returned from a function is not a named variable
takes_nonconst_reference(returns_int());
In this particular case, since your class is storing a copy of the constructor parameter, you should pass it by value (int, not int& nor const int&).

Related

Why a const variable cannot be passed by reference?

My lecture notes said
The argument to a reference parameter must be a variable, not a
constant or an expression.
And thus
int f(double & var); // function prototype
...
const double t = 4.0;
int ret = f(t);
f(t) is illegal.
But I do not understand, why would t be illegal. t is a constant, but still a variable, and I don't think there's anything wrong passing t by reference.
What if the function f modifies var? That shouldn't happen if t is const.
Here's an example implementation of f:
int f(double & var)
{
var += 1;
return var;
}
This will change whatever is passed as an argument. But if the argument was const... tough luck. Then it's not allowed and the compiler explicitly tells you this.
This is the error generated by the compiler:
error: binding reference of type 'double&' to 'const double' discards qualifiers
So by passing a const variable into the function (without a non-const argument), you're telling the compiler to neglecting the constness of the variable in the first place.
If you wish to pass it by reference, pass it by const-reference:
int f(const double & var) // or int f(double const& var)
{
var += 1;
return var;
}
This tells the compiler to retain the const-ness of its arguments.
Let me enhance my comment to an answer:
First, t is not a constant, but a const variable. A constant would be 4.0. Your lecture notes are basically saying that you cannot do something like int ret = f(4.0);
Second, what you are seeing is a type mismatch. const as a qualifier is part of the type. You cannot do the following:
const int x = 1;
int& ref_x = x;
error: binding reference of type ‘int&’ to ‘const int’ discards
qualifiers
Nevertheless, it is legal to pass const qualified variables as reference, either use a const reference or cast away the const:
Use a const reference const int& const_int_ref = x;
Use const_cast: int& rx = const_cast<int&>(x);
I prefer the first one whereever possible.
When you have a reference parameter the object passed needs to actually (at least be able to) occupy memory, a constant (as opposed to a const variable) does not.
I.E. the following would be okay:
void foo(int & n) {
n = 3;
}
void bar() {
int i;
foo(i);
std::cout << "i is " << i << std::endl;
}
but if you had:
void qux() {
foo(3);
}
there would be no object for the assignment in foo to assign to.
Note that you can pass a constant as a reference-to-const (i.e. MyType const &), that's allowed because the assignment issue does not exist when the reference is to a const.

Unclear type conversion/promotion

The below code compiles and runs without warning.
It's not clear to me what kind of conversion goes on in the call to func. The function expects a reference to type B, yet it accepts a pointer to type A. When removing the constructor B(PA A) the compilation fails, claiming failure to initialize reference type:
Invalid initialization of reference of type 'const B&' from expression of type 'PA {aka A*}'
I don't see why the constructor is invoked in the first place instead of compilation failure. Any explanation to this?
#include <iostream>
typedef struct A
{
int x;
int y;
} *PA;
class B
{
public:
B(PA pa):m_pa(pa){}
int getPa() const{return m_pa->x;}
private:
PA m_pa;
};
void func(const B& b)
{
std::cout << b.getPa();
}
int main()
{
A a = {5,7};
PA pA = &a;
func(pA); //Why does this compile and what is the outcome??
return 0;
}
Live: https://ideone.com/y8Z2O9
As explained here:
Single-argument constructors: allow implicit conversion from a
particular type to initialize an object.
Hence, there is an implicit conversion option from PA to B due to the constructor:
B(PA pa)
So when you called:
func(pA)
the pA pointer converted to B object and then used by func
(similar SO post)

Function parameters: const matching declaration and definition [duplicate]

This question already has an answer here:
Use of 'const' for function parameters
30 answers
Found this by accident where a function declaration and definition may not agree on the constness of parameters. I've found some information (links following), but my question is why is const matching optional for by-value parameters, but const matching is required for reference parameters?
Consider the following code available here.
class MyClass
{
int X;
int Y;
int Z;
public:
void DoSomething(int z, int y, const int& x);
int SomethingElse(const int x);
void Another(int& x);
void YetAnother(const int& z);
};
void MyClass::DoSomething(int z, const int y, const int& x) // const added on 2nd param
{
Z = z;
Y = y;
X = x;
}
int MyClass::SomethingElse(int x) // const removed from param
{
X = x;
x = 3;
return x;
}
void MyClass::Another(int& x) // const not allowed on param
{
X = x;
}
void MyClass::YetAnother(const int& z) // const required on param
{
Z = z;
}
I've found this on SO, but it is looking for explanation for name mangling. I've also found this on SO and this on SO, but they don't go into detail on why const matching is required for reference parameters.
When you pass by value, the argument is effectively a local variable of the function. Whatever you pass is copied. If the argument is a const T, it just means the function itself cannot modify its own variable. The caller shouldn't know or care about that.
Passing by const T& actually refers to access of a variable which does not belong to the function. Same with const T*. But not the same with T* const, that one would just mean the function itself cannot modify its own pointer. The pointer belongs to the function, if the function wants to reassign it to point to something else that's its own business. What it points to does not belong to the function, so whether the function gets const access or not is very relevant to the caller.
For value parameters, const actually makes no difference. If a parameter is passed as value, it cannot be modified by the function anyway.
If an argument is passed by value but marked as const, what would happen if the variable did get modified? The copy local to the function would change, but the copy that got passed in would not.
However, on the other hand if passed by reference, if you could somehow modify the variable it would manipulate the original and not just a copy.

Confusing L-Value and R-Values parentheses

In this example here at the bottom, there are exemplary l-values defined:
// lvalues:
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue
I am not shure what foo() is here? At first sight it looks like a function/method?
Is int& foo() the same as int& foo; ?
But on the other hand, my compiler says
Error: 'foo' declared as reference but not initialized int & foo;
Same with the rvalues foobar():
// rvalues:
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
Yes, it is "a function/method", returning a reference to an int. Or, more precisely, this is a declaration of such a function, not definition: it says that such function exists, but does not provide the actual code (because the code is not relevant for the example). Compare how you define functions in header files.
A possible example of code for a similar function:
int a, b;
int& foo(bool which) {
if (which) return a;
else return b;
}
...
foo(true) = 10; // works as a = 10;
Yes, foo is indeed a function. The example is just trying to say "if a function returns an lvalue reference, then the result of calling it is an lvalue."
They probably just mean to say "there is a function foo returning an int& defined." Although technically, the code is correct as-is: it is possible in C++ to declare functions locally:
int main()
{
int foo();
return foo();
}
int foo()
{
return 42;
}
The example is referring to the value category of the function call expression foo(). In this case, foo() is an lvalue because foo returns an int&.

c++ syntax in understanding rvalue references

I was reading a blog about c++11 rvalue references by Thomas Becker, and the following syntax near the bottom of the page really confuses me.
int& foo();
foo() = 42; // ok, foo() is an lvalue
What exactly is foo? A function pointer that returns a int; an object?
If a function object, how can you assign a value to it, and why is foo a lvalue?
foo is a function with return type int&. The line
int& foo();
simply declares the function, but does not define it.
When foo is called, the result is an lvalue of type int. Therefore you can assign to it: foo() = 42;
int& foo(); declares a function foo that takes no arguments and returns a reference to an int. For example,
#include <iostream>
int a = 0;
int& foo();
int main()
{
std::cout << a << std::endl;
foo() = 42;
std::cout << a << std::endl;
}
int& foo() { return a; }
Output:
0
42
Every function has at least one return expression. Let's look at function calling using the following incomplete, simplified description :
Assume your function signature is T f();, with one return expression return e;. For the sake simplicity, lets also assume that e and T have the same type after removing references and const-ness. In this situation, you can go to your function call location and replace the call f() with (T)e, to understand what's going on.
int& f(){....; return e;}
.
.
.
f() = 5;
becomes
(int&)e = 5;
Of course, if converting e to an lvalue reference(int&) was invalid, the compiler will throw an error.

Resources