This question already has answers here:
Why not non-const reference to temporary objects? [duplicate]
(4 answers)
Closed 6 years ago.
Lets say I have class A:
class A {
int i;
public:
A(){};
A(int i){this->i=i;};
};
And a simple test function:
void test(const A &a){...}
Now, if I do:
int main()
{
test(2);
}
It compiles and it will call the A(int i) constructor. But when I change the argument to be non-const: void test(A &a) I get a compilation error.
What is the difference between those cases, why the first one is allowed and the second not, and what actually happens in the initialization of the first case?
The difference between the two cases is that C++ compiler is allowed to create temporary objects to be passed to functions accepting a const references, but functions accepting non-const references must be provided with actual objects.
When you call test(2) what actually happens is this:
A hidden(2);
test(hidden);
The compiler creates hidden object, initializes it with 2, and passes the result to test. Since test is guaranteed to not modify A, this is fine.
When test does not provide such guarantee: imagine test that sets a new value:
void test(A& a) {
a.i++; // let's pretend "i" is public
}
If you call test with an actual object, i.e. A a(3); test(a); you can harvest the result of the update from a after test return. Calling test(2), on the other hand, gives you no way to access the result of an update. This indicates a potential error in the logic, so the compiler treats it as an error.
This is an interesting case. using const you can bind a reference to an rvalue. A more simpler example is below.
int get_num()
{
return 2;
}
int main(){
int& p = get_num(); // This is a compiler error. Can't create a non-const reference to an rvalue
const int& q = get_num(); // this will work. Can create const reference to an rvalue
}
This is part of the c++ standard.
what actually happens in the initialization of the first case?
In the 1st case, a temporary A will be constructed implicitly by A::A(int) and then bound to the lvalue reference to const. The effect of test(2); is same as test(A(2));.
Temporary can't be bound to lvalue reference to non-const, that's why the 2nd case failed.
Related
This question already has answers here:
Reference a temporary in msvc
(2 answers)
Closed last year.
I observe a different behavior for the compiler with the 2 following snippets of code:
With the first one, all seems OK to me with fundamental type like int:
void fi1(int& a) {}
void fi2(int&& a) {}
void func1() {
fi1(2+2); // Do not compile: normal since '2+2' is temporary and is not a variable
int a = 5;
fi2(a); // Do not compile: normal since 'a' is an lvalue
}
But I do not understand the second one with object type:
void fs1(string& a) {}
void fs2(string&& a) {}
void func2() {
fs1(string{""}); // Compile: but weird for me since there is no variable to reference !
fs2(string{""}); // Compile: normal since 'string{""}' is temporary and is not a variable
}
In the following post:
Understanding lvalue/rvalue expression vs object type
it is explained that an lvalue is when you can take its address. For sure, string{""} shall have an address somewhere in memory. But for me a key-point with rvalue is that you can't use it afterwards, making 'move semantic' possible.
When you are in fs2 function, the argument is an rvalue and you can do whatever you want without any side effect. In fs1, you expect that some variable elsewhere will have some impact if you modify it. fs2 should only be called with variable argument.
And why is working as expected with int and not with object ?
This strange behavior is due to the usage of MSVC.
Suppose I've got something simple like:
void f(int a){...}
int main()
{
f(3);
return 0;
}
How does the initialization happen on a slightly lower abstraction level when we call f(3) ? Does a temporary object gets created with a value of 3 or does it just initialize by copy-initialization ?
Primarily the reason for asking this was when I encountered that in this code:
void f(int a){...}
void f(int&& a){...}
int main()
{
f(3);
return 0;
}
...I get an error saying it is ambiguous. Since I'm quite sure that calling the function f(int&& a) we get a creation of temporary object which gets a lifetime extension with a reference a, I would also say that calling the f(int a) also invoke the creation of a temporary. Otherwise, shouldn't compiler choose to call f(int a) as being more effective?
Also is there a book maybe which covers this topic elegantly ?
Semantically (forgetting about inlining, optimization and stuff) the first snippet does not require a temporary.
void f(int a){...}
When you call this function as in f(3), an integer object is created using (pseudo) copy-constructor of int to be used as function argument. This becomes a local parameter to the function, and it's life-time ends when function returns.
Meanwhile, the second snippet
void f(int&& a){...}
requires calling code to create a temporary int variable (as you can't bind a reference of any kind to numeric literal). After that an rvalue reference is bound to created temporary.
And to your question of 'efficiency', compilers do not select function overloads based on efficiency. They do this based on a certain ranking of conversions, and in your case, copy and a reference binding have the same rank. You can read more about overload resolution here: https://en.cppreference.com/w/cpp/language/overload_resolution
This question already has answers here:
Why not non-const reference to temporary objects? [duplicate]
(4 answers)
Closed 6 months ago.
For some reason I didn't manage to find this exact question. Why is it allowed to bind an rvalue to const lvalue reference, although it is impossible to to the same without the const?
I do understand that the lifetime of the rvalue gets an extension somehow (in the first case) , but if so, why would the compiler disallow changing that 'rvalue', which is not really a temporary object anymore.
For example, consider the following code:
int main(){
int &i=3; //produces error
const int&j =3; //compiles
return 1;
}
You may find the following article useful:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0345.pdf
I might be entirely wrong here, but this is how I rationalise it. An rvalue is constant, it cannot be changed. you cannot change the integer 5, fact. So when you bind the references the lvalue will have to be const. Otherwise your compiler will throw an error:
obj & a1 = bar();
invalid initialization of non-const reference of type ‘obj&’ from an rvalue of type ‘obj’
using g++
The only way to safely bind an rvalue to an lvalue is either by marking the lvalue as const, or using a mutable rvalue reference && (introduced in C++11 believe?)
struct obj {
};
obj bar() {
obj x;
return x;
}
int main() {
const obj & a1 = bar();
obj && a2 = bar();
return 0;
};
If you're asking about
void f(const int&);
f(1);
versus
void g(int&);
g(1);
the answer is to think about what happens if g looks like this:
void g(int& r) {
++r;
}
The name r-value comes from the right-value: which (roughly) means things that are on the right side of a x=y kind of statement. It is like: it can be read from, but it may not be suitable to be written on. For example a numeric literal can be on the right side (a=3) but it does not make sense to be on the left side (3=a).
With this reasoning, it looks logical to me why it is allowed to make const l-value references to r-values but not allowed to make non-const references.
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.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 9 years ago.
Improve this question
I have the following program
#include <iostream>
class A
{
public:
A(int n = 0)
: m_n(n)
{
std::cout << 'd';
}
A(const A& a)
: m_n(a.m_n)
{
std::cout << 'c';
}
private:
int m_n;
};
void f(const A &a1, const A &a2 = A())
{
}
int main()
{
f(3);
std::cout << std::endl;
return 0;
}
This program produces "dd" as output. I don't understand why constructor is called for the first parameter of the function "f". I am passing it an integer "3" and it calls constructor for "a1" with argument "3". How is this happening ?
This constructor
A(int n = 0)
: m_n(n)
{
std::cout << 'd';
}
is a conversion constructor. It converts an object of type int (in this case this object is specified by parameter n) to an object of type A. So when you call function f as
f( 3 );
the compiler sees that there is a function with such name that has the first parameter of type const A &. So it tries to convert 3 to an object of type A and it can do this due to the conversion constructor that it calls implicitly. Then a reference to this temporary object is passed to the function. If you would declare the constructor as
explicit A(int n = 0)
: m_n(n)
{
std::cout << 'd';
}
then the compiler could not call it implicitly and issue an error.
As for the second parameter then it has a default argument: an temporary object of type A. The compiler can bind a const reference to a temporary object.
So in your code the conversion constructor is called twice. For the first argument it is called implicitly by the compiler and for the second argument it is called explicitly because its call is specified as the default argument.
The copy constructor does not take part in this process. You do not copy any object. You only bind temporary objects with const references.
A::A(int n); gets called with n set to 3. The object is a temporary which is passed to f which takes it by a const reference to A.
In your code the copy constructor never gets called.
Compiler tries to match function parameter with what was given. He got int 3 but needs either A(int n) or A(const A& a). Compiler then notes that he can easily create temporary object A that takes and int and feed that to the function. So that's what it does.
The copy constructor isn't called at all, for the run that produced your output.
And the explanation of that is that the copy constructor call (for the second argument) was elided, optimized away. The explanation is that both formal arguments are references, as implicitly pointed out by Steve Jessop in comments (I was a bit blind there).
Both the first and second formal arguments of f are constructed with calls to the int argument constructor, since it doubles as default constructor.
When you pass objects around by value, the copies are created using one of the available constructors. That may be a copy constructor (that takes an A and results in another A) or it may be a constructor that takes something completely different (and results in an A)!
This is a basic fact of object construction and has nothing to do with references:
struct A
{
A(int x) {};
};
void foo(A a) {}
int main()
{
foo(3); // OK
}
In your case you're not passing by value, but C++ still tries to form a A for you in order to get your function call to work. When successful (as here), the result is a temporary A (which can be bound to the const reference).
Ultimately, this is like:
int main()
{
A a(3);
foo(a);
}
Being able to pass things into function calls that then have conversions applied is a crucial tool in C++'s flexibility.