Is the hard coded value in function argument a const reference? - c++

Why does the following program fail to compile:
#include<iostream>
using namespace std;
int fun(int &x)
{
return x;
}
int main()
{
cout << fun(10);
return 0;
}
It gives the following compilation error:
invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
To make it compile successfully , I have 2 options:
1. we have to use "int fun(const int &x)" instead of "int fun(int &x)"
2. use "int i=10;cout << fun(i);" instead of "cout << func (10)"
So, it seems like , if we pass a hard coded value to a function , it will be treated like "const reference".
Am I right here? or is there any other reason that above program doesn't compile?

This doesn't compile because non-const lvalue references cannot bind to rvalues.
Think about it this way: if x in fun is a non-const lvalue reference, we should be able to modify it. However, we have passed in the integer literal 10. What should it mean to modify an integer literal? That doesn't make sense, so you can't.
To fix this, you should take your argument in by reference-to-const (assuming you don't plan on modifying it in fun):
int fun(const int &x)

Related

std::forward through an example

I would like to go over an example using std::forward because sometimes I can make it work and some other times I can’t.
This is the code
void f(int&& int1, int&& int2){
std::cout << "f called!\n";
}
template <class T>
void wrapper(T&& int1, T&& int2){
f(std::forward<T>(int1), std::forward<T>(int2));
}
int main(){
int int1 = 20;
int int2 = 30;
int &int3 = int1;
wrapper(int1, int2);
}
I am passing int 1 and int 2. These are lvalues. They are silently converted to &int1, &int2. These are converted using &&. But reference collapsing keeps them just &int1, &int2.
f takes && parameters
If I pass simply int1 and int2 as they are I am passing &int1, &int2. This does not work.
So I pass std::forward(int1) std::forward(int2).It should be the same as using static_cast<T&&>. Because of this, thanks to referencing collapsing I can pass to every function f (theoretically even one that accepts only l-value references).
My code does not compile and my logical reasoning has probably some contradictions.
candidate function not viable: no known conversion from 'int' to 'int &&' for 1st argument
void f(int&& int1, int&& int2){
How on earth did I get a simple int after using all these ampersands?
Additional question: My compiler asks me to use wrapper<int &> instead of only wrapper(some parameters). Can I just leave it like in my code, or I need to manually put wrapper<int &> (this is what my compiler is asking me to add). Why do I need <int &> int this case?
The whole problem stems from the forwarding references using same symbols as rvalue ones, but not being the same.
Take the following code:
template<typename T>
void f(T&& t)
{
//whatever
}
In this case T&& is a forwarding reference. It is neither T&& in the sense of rvalue-reference (reference to temporary), nor is it T&. Compiler deduces that at compile time. Notice though, it does not have type specified either, it's a template paremeter. Only in case of those parameters the forwarding reference semantics applies (another way of writing it down is a auto&& in lambdas, but the deduction works the same way).
Thus when you call int x= 3; f(x); you're effectively calling f(int&). Calling f(3) calls effectively f(int&&) though.
void g(int&& arg)
arg is and rvalue reference to int. Because the type is specified, it's not a template argument! It's always an rvalue reference.
Thus, for your case
void f(int&& int1, int&& int2){
std::cout << "f called!\n";
}
template <class T>
void wrapper(T&& int1, T&& int2){
f(std::forward<T>(int1), std::forward<T>(int2));
}
int main(){
int int1 = 20;
int int2 = 30;
int &int3 = int1;
wrapper(int1, int2); //call wrapper(int&, int&);
//Then f(std::forward<T>(int1), std::forward<T>(int2));-> f(int&, int&), because they are lvalues! Thus, not found, compilation error!
}
Live demo: https://godbolt.org/z/xjTnjcqj8
forward is used to convert the parameter back to the "valueness" it had when passed to the function. We can see how this works using this example
void foo(int&) { std::cout << "foo(int&)\n"; }
void foo(int&&) { std::cout << "foo(int&&)\n"; }
template <typename T> void wrapper(T&& var) { foo(std::forward<T>(var)); }
int main()
{
int bar = 42;
wrapper(bar);
wrapper(42);
}
which outputs
foo(int&)
foo(int&&)
So, when you pass wrapper an lvalue, forward will forward that along, and the lvalue accepting overload of foo is called. When you pass an rvalue to wrapper, forward will convert var back into an rvalue1 and the rvalue overload of foo is called.
Since your f function only accepts rvalues, that means your wrapper function will also only work for rvalues. You are basically just trying to do f(int1, int2) in main, and that wont work.
The reason you get the error message no known conversion from 'int' to 'int &&' is that it is trying to tell you that there is no conversion from an lvalue int into a reference to an rvalue int.
1: This is needed because as a named variable, it is an lvalue, even if it is a reference to an rvalue.
I'll start off with something mentioned in the comments, and that is that having only one template parameter here relates the two parameters and interferes with the usual deduction process. Normally when forwarding, you deduce a type for each forwarded parameter independently. In this case, it will still work, but only because the call site passes two things with the same type and the same value category (lvalue, rvalue, etc.).
Here's what a typical wrapper would look like, truly forwarding the arguments independently:
template <class T, class T2>
void wrapper(T&& int1, T2&& int2){
f(std::forward<T>(int1), std::forward<T2>(int2));
}
With that out of the way, I'll move on to the intuitive reason your code doesn't compile. If forwarding is done correctly, the wrapper function's existence won't change anything. By the above reasoning, your wrapper function meets this criteria for this particular call. Let's see what happens when it's gone:
void f(int&& int1, int&& int2){
std::cout << "f called!\n";
}
int main(){
int int1 = 20;
int int2 = 30;
int &int3 = int1;
f(int1, int2);
}
You might be able to spot the error more clearly now. f takes rvalues, but it's being given lvalues. The wrapper preserves the value category of its arguments, so they're still lvalues when handed to f. To fix this with the wrapper, it's the same as without—pass rvalues:
int main(){
f(20, 30);
// Alternatively: f(std::move(int1), std::move(int2));
}
Now, reviewing the points made:
I am passing int 1 and int 2. These are lvalues. They are silently converted to &int1, &int2. These are converted using &&. But reference collapsing keeps them just &int1, &int2.
Don't think about the variables themselves here, but the types. Because you pass in two lvalue ints, T is deduced to be int&. Following that, the actual parameter types, T&&, are then int& as well because of reference collapsing. Thus, you have a function stamped out with two int& parameters. In the forward calls, T is int&, so its reference-collapsed return type is again int&. Thus, the call expressions have the type int and are lvalues (specifically because the return type is int&—the language calls that out as an explicit rule).
As a side note, &int1 isn't clear to me because its C++ meaning is taking the address of int1, an entirely irrelevant meaning here. I think what you're trying to say is that it's an lvalue or that a parameter's type is an lvalue reference.
If I pass simply int1 and int2 as they are I am passing &int1, &int2. This does not work.
This is true for the the reasons discussed earlier. The parameters are themselves lvalues, so a function taking rvalues won't accept them.
So I pass std::forward(int1) std::forward(int2).It should be the same as using static_cast<T&&>. Because of this, thanks to referencing collapsing I can pass to every function f (theoretically even one that accepts only l-value references).
Yes, this is the point of forwarding. It lets you preserve the value category of the arguments given to the wrapper, so the same template can be used to pass along arguments to a function regardless of which value categories it accepts. It's up to the caller of the wrapper to provide the correct value category in the first place and then the wrapper simply promises not to mess with it.
How on earth did I get a simple int after using all these ampersands?
This is just how the type is displayed in the error message. Although the parameter is an int&, the expression has the type int. Remember that an expression has both a type and a value category and that they're separate properties. For example, you can have an rvalue reference parameter int&& x and the expression x will still be an int and an lvalue.

C++ cannot convert input param to reference

Below is a simple example that produces errors (VS 2019):
E0461 initial value of reference to non-const must be an lvalue
C2664 'void foo(std::string &)': cannot convert argument 1 from 'std::string' to 'std::string &'
using namespace std;
void foo(string& input) {
cout << "Foo is: " << input << endl;
}
string getInput() {
return string("abc");
}
int main()
{
foo(getInput());
}
I don't understand the error. getInput() returns a string. Why can't that string be passed directly to the foo() function as a reference just like any local or member variable can be passed by reference to the foo() function? As the error message suggests, making the string argument a const makes the error go away. Why would that be?
Update: I thought about this question some more after considering the response and a couple links that I posted below. I think the core issue here is that the return value of getInput() is a temporary variable. If I had instead copied the result of getInput() into a local or global variable and then passed that to foo(), there would be no error. I think the compiler does not "like" passing the temporary variable that stores the result of getInput() by reference because how long will the temporary variable stay valid? Obviously, it must stay valid immediately after getInput() returns, but how much longer after that?
This is because a const reference can bind to both lvalue and rvalue. This makes sense as being marked as const, the value cannot be changed even if the input is an rvalue. If the value is not const, you then have the option of editing the non-const reference inside the function. This would make no sense as the input was an rvalue.

error: invalid initialization of non-const reference of type 'std::function<void()>&' from an rvalue of type 'main()::<lambda()>'|

EDIT: Sorry, I asked this question without a thro understanding of references...
I seem to be getting this error when I run this code...
error: invalid initialization of non-const reference of type 'std::function&' from an rvalue of type 'main()::'
#include <bits/stdc++.h>
using namespace std ;
void printfunction(bool a, function <void()> &b)
{
if (a == true)
{
b() ;
}
}
int main()
{
int value = 45 ;
printfunction(true, [value](){cout << "The value is : " << value ;}) ;
}
But, the error disappears when I add a const before function... like this :
void printfunction(bool a,const function <void()> &b)
The thing is I would like to change the function in the function reference if needed...
Is there any other way to do this? Please let me know if it does indeed exist.
Bye,
Samuel
In printfunction call, lambda expression [value]() {...} argument must be converted to a temporary function<void()> object first.
A reference to non-const function<void()>& only binds to l-values, not temporaries (r-values).
A reference to const, on the other hand, can be bound to temporaries. Which is what you observe.
If you want to modify the std::function, then you'll need to pass a modifiable (lvalue) parameter:
int main()
{
int value = 45;
std::function f = [value](){ std::cout << "The value is : " << value ;};
printfunction(true, f);
}
What you were trying to do isn't much different from writing a function that takes a mutable reference to int (e.g. void foo(int& x)) and then complaining that you can't call foo(5). (The small difference is that the the lambda-expression is converted to a temporary std::function - but that still can't be bound to a non-const reference).
An alternative would be to change printfunction to take its argument by value rather than by reference, so that it has its own copy which it may modify. You'll have to consider the needs of the caller to decide whether that's more appropriate.

Converting arrays into vectors inside function argument

Let's imagine that there is some data stored into an array that needs to go through a function that accepts vector. In this situation, clearly the array data needs to be converted into the respective vector type. One general approach would be
std::vector<int> vec(arr, arr+n);
function(vec);
where arr is the mentioned array variable. I know that we added just one line, but its looks somehow as an unnecessary code pollution. So I tried this
function(std::vector<int>(arr, arr+n))
what worked. Bellow there is a detailed code this.
#include <vector>
#include <iostream>
void print(std::vector<int> vec){
for(unsigned int i=0; i!=vec.size(); ++i){
std::cout << vec[i] << std::endl;
}
}
int main(){
int a[] = {2,1,4,5,6};
print(std::vector<int>(a,a+5));
}
From this, my first question is: This approach is ok, or it has some undesired behavior?
After that, I decided to change the function argument to accept a vector reference like
void print(std::vector<int> &vec){
for(unsigned int i=0; i!=vec.size(); ++i){
std::cout << vec[i] << std::endl;
}
}
what did not work. Here is the error that I got
test.cpp: In function ‘int main()’:
test.cpp:13:34: error: invalid initialization of non-const reference of type ‘std::vector<int>&’ from an rvalue of type ‘std::vector<int>’
print(std::vector<int>(a,a+5));
^
test.cpp:4:6: error: in passing argument 1 of ‘void print(std::vector<int>&)’
void print(std::vector<int> &vec){
Here is the second question: Why, when the function argument as chanced to a vector reference this approach did not work. There is someway to get around this compiler error keeping the approach of creating the vector object in the argument function or not?
The problem is binding a rvalue (std::vector<int>(a,a+5) in the function call) to a lvalue reference (std::vector<int> &vec in the parameter list of the function). A rvalue can bind to a const lvalue reference, so as long as the parameter will not be changed the simplest solution is to write
void print(const std::vector<int> &vec)
which by the way saves you one copy operation on the vector. Or you could write a function overload binding explicitly to rvalues by having a rvalue reference parameter.
void print(std::vector<int>&& vec)
But you have to be aware of the fact, that this overload will only bind to rvalues. So you will have to maintain two functions, one for lvalues and one for rvalues.
For further reference on lvalues and rvalues, see for example Understanding lvalues and rvalues in C and C++ or ask your favourite search engine ;-).
In this line print(std::vector(a, a+5)), the compiler creates a temporary object which is a rvalue reference. Here we are trying to pass a reference value to a function which accepts non const lvalue reference, hence the compiler error. Instead if you declare print() method to accept a rvalue reference then you can get rid of the compilation error. Please see the code snippet below
#include <vector>
#include <iostream>
void print(std::vector<int> && vec) {
for(unsigned int i=0; i != vec.size(); ++i) {
std::cout << vec[i] << std::endl;
}
}
int main() {
int a[] = {2,1,4,5,6};
print(std::vector<int>(a,a+5));
}
The more on lvalue, rvalue reference, please refer http://en.cppreference.com/w/cpp/language/reference

invalid initialization of non-const reference of type 'int&', what the reason?

I have the given code, which gets an error:
error: invalid initialization of non-const reference of type 'int&'
from an rvalue of type 'int' const int b = f(a++);
^
int f(int& a)
{
return a;
}
int main() {
// your code goes here
int a = 5;
int b = f(a++);
std::cout << b << std::endl;
return 0;
}
What the cause of this error ?
You can't bind a temporary to a non-const reference.
Post-increment (a++) increments a and returns a temporary with a's old value.
Why are you passing by non-const reference? - it doesn't look like you're changing the parameter inside the function, just just pass by value or const reference.
If you were changing the parameter, what would you expect the behavior to be, considering a++ already changes it? Would the change be intuitive? Legal?
The postfix increment operator on an int returns a temporary value. A temporary value cannot bind to a non-const lvalue reference, because modifying that temporary doesn't make sense. You are trying to bind the temporary to an int&, which is giving an error.
To fix this, either use the pre-increment operator (++a), or take your argument by value (it's better to pass builtin types as value rather than const T&):
int f(int a)
{
return a;
}
This function:
int f(int& a)
accepts non-const reference. Such references must always point to a valid objects, residing at certain memory locations (*).
Post incrementation works as follows:
- save current value as `r`
- increment original variable
- return `r`
That's because result of post-incrementation is a temporary, yielding value from before incrementation. Such temporary must be passed either as value or const reference:
int f(int a) //This will work
int f(const int& a) //And this
(*) In fact, older compilers allowed for such constrcuts. For example, this code will compile under VC6:
struct T {};
void f(T& t)
{
}
int main()
{
f(T());
}
However, such behaviour is not standard-compliant.