The following does not compile:
#include <iostream>
using namespace std;
int x = 5;
int && f () { return std::move(x); }
int g(int & y) { return y; }
int main() {
g(f());
return 0;
}
It's clear to me why prvalues (unnamed temporaries) do not bind to non-const lvalue references -- it does not make sense to modify them, as they will soon disappear. Yet why do xvalues not bind to non-const lvalue references?
If a function returns int &&, the referenced object can't be temporary, otherwise we would get a dangling reference. Hence if an int && is returned, that's, in my understanding, a reference with the additional guarantee that it's safe to move from it.
Edit: Wording corrected: "Values" bind to "references" and not vice versa.
Second edit: The following compiles -- I do not see the conceptual difference, besides y now being an lvalue. Yet it still references x. I understand why this should compile and the above shouldn't, by the language specification. I do not, however, understand the reason behind it. Why does mere aliasing change the picture?
#include <iostream>
using namespace std;
int x = 5;
int && f () { return std::move(x); }
int g(int & y) { return y; }
int main() {
int && y = f(); // reference!
g(y); // compiles
// check that y indeed references x
y = 7;
std::cout << x << std::endl; // prints 7, of course
return 0;
}
Third edit: In short, what's the idea behind not allowing
int && f() { ... }
int g (int & y) { ...}
g(f());
yet allowing
int && f() { ... }
int g (int & y) { ...}
int & k (int && y) { return y; }
g(k(f()));
Because that would make a mess.
The whole point of non-const lvalue references is that they are aliases for non-temporary objects whose lifetime is already being managed by some other means. The introduction of rvalue references — and, crucially, std::move — was specifically to create a new "class" of references whose binding signifies that the referent is most likely "safe" to move from.
If these referents also were able to bind to a simple T&, you'd have a slew of ambiguous conversion errors and nobody would know what to do. You could give such conversions a lower rank, but I feel that this would still be extremely confusing.
As such, you're looking at it backwards. Ask yourself instead why xvalues don't bind to non-const lvalue references.
Related
I have this small piece of code that doesn't compile, and I was wondering about the reasons more specifically why is push(new X) the problem?
Before that, I would like to ask what new X (with no identifier) really mean? does it create an object X with no identifier name? does it go through the constructor at all?
Secondly,
I might not understand the whole concept, but, push is a template class, right?
Since it contains a static T stack, the type T is consistent and can not be changed once a specific argument is sent, the first one, in this case int x. Is that correct?
Thirdly,
following my second question, if it is correct then why push(y) do not flag a compiler error just like push(new X) does? i mean, x is an int type and y is a reference type.
I'd really appreciates if someone can clarify it for me.
Thanks, here it is:
#include <iostream>
using namespace std;
template <class T>
void push(T &t) {
static const int CAPACITY = 20;
static T stack[CAPACITY];
static int size = 0;
stack[size++] = t;
cout << size;
};
class X {};
void main()
{
int x = 3;
int &y = x;
push(x);
push(new X);
push(y);
}
and I was wondering about the reasons more specifically why is push(new X) the problem?
Because you declared type of your template function parameter as non const lvalue reference to T. To help programmers to avoid incorrect code language does not allow to bind temporaries to non const lvalue references. In your case:
new X
returns a temporary of type X * (pointer to X), your template function argument type T deduced to X * in this case and t is now lvalue reference to X * or X *& but language does not allow to bind temporary to an lvalue reference hense compilation error. Your second case is logically equal to this code:
void pushX( X *&t ); // pushX accepts reference to pointer to X
pushX( new X ); // you cannot bind a temporary returned by `new X` to lvalue reference `t`
It could be easier to understand if you use simple type like int:
int function_returns_int();
void push_int( int &ri );
push_int( function_returns_int() ); // compilation error, function_returns_int() returns temporary
You can make your code compile if you store pointer in a variable and make it non-temporary:
int main() // main() must have return type int not void
{
int x = 3;
int &y = x;
push(x);
X *px = new X;
push(px);
push(y);
delete px;
}
but most probably you chosen wrong type for the argument.
Details about references and why you can pass int &y and why t would not be a reference to reference in this case you can find here
Note: your code if you make it compile (changing type of t to const reference for example) would lead to memory leak, but that is out of scope of your question
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.
In C++11, if I try to do this:
int x = 5;
int && y = x;
It will fail to compile, with an error telling that an r-value reference cannot bind to an lvalue.
However if I do:
int x = 5;
auto && y = x;
It compiles with no errors. Why is it happening? I tried to get the type of y but typeid() takes away the reference attributes. Does auto && automatically collapses to a & or && depending on what is being assigned?
In the first case int && y, the variable y can bind to only rvalue which x is not.
In the second case auto && y however, the variable y can bind to anything, as the type of y would be deduced anyway — and reference-collapsing will be applied accordingly — which is why your code compiles:
auto && y = x;
Since x is an lvalue, auto is deduced to be int&, hence it becomes:
int& && y = x;
and after reference-collapsing, it becomes:
int & y = x;
which is fine.
To understand it in more detail, read about:
Universal Reference (or Forwarding Reference, as it has been proposed to improve the terminology)
Reference Collapsing
Hope that helps.
int&& is an r-value reference to int.
Whereas auto&& is a universal reference.
In the second example, auto (in auto&&) would be anything which would make the code well-formed. In this case, int& && is well-formed, since int& && collapses to int& - thus all work well.
So here is my little class:
class test{
public:
int& addressofx();
private:
int x;
};
int& test::addressofx(){
return x;
}
and here is main:
int main(){
test a;
int& foo = a.addressofx();
}
Why does the above compile fine, but when I make the function addressofx into a regular int function(not int&), I get errors?
You can do this:
int x;
int &y = x;
without getting the address of x, but when you return x from a function you need its address? Why?
You're not returning the address of anything. You're returning a reference.
This code will return an address:
class test {
public:
int* addressofx();
private:
int x;
};
int* test::addressofx() {
return &x;
}
int main() {
test a;
int* foo = a.addressofx();
}
int& addressofx();
This is returning a reference to an int variable.
int& foo = a.addressofx();
This is declaring a non-const lvalue reference (lvalue means it has a variable name assigned to it, ie it can appear on the left side of an expression) that holds a reference to an int variable. Since the function returns a reference to an int, the compiler is happy.
when I make the function addressofx into a regular int function(not int&), I get errors?
A non-const lvalue reference cannot be bound to a temporary value (what addressofx() would return if you make that change). That is what rvalue references (introduced in C++11) are designed to handle instead, but you are not using that feature in this example.
You can do this:
int x;
int &y = x;
Because you are declaring an lvalue reference and binding it to an lvalue variable. That is allowed.
If you change addressofx() to return an int (which is an rvalue, because it has no name so it can only appear on the right side of an expression), the return value cannot be bound to a non-const lvalue reference, because it is not a real variable. However, it can be bound to a const lvalue reference (and the compiler will then extend the lifetime of the temporary beyond the function's return), eg:
int addressofx();
...
const int& foo = a.addressofx();
In short, you need to read up on how references work, and the difference between lvalue and rvalue. Not all combinations can be used together.
The following code yields a Segmentation Fault on the y = anotherFunctor() line. As far as I understand, this happens because the globalFunctor variable does not exist when anotherFunctor is created. But why does it work if I replace std::function<int(int)> with GlobalFunctor? How would I fix it?
#include <functional>
struct GlobalFunctor
{
int operator()() const { return 42; }
};
extern GlobalFunctor globalFunctor;
struct AnotherFunctor
{
AnotherFunctor() : g_(globalFunctor) {}
int operator()() const { return g_(); }
const std::function<int()>& g_;
} anotherFunctor;
GlobalFunctor globalFunctor;
int main()
{
AnotherFunctor af;
int x = af();
int y = anotherFunctor();
int z = x + y;
return 0;
}
Edit: I tried compiling this with clang instead of gcc and it warns me about binding reference member 'g_' to a temporary value -- but it crashes when compiling this. Would the cast to std::function create a temporary reference?
At g_(globalFunctor), globalFunctor has to be converted to an std::function because it is of type GlobalFunctor. So a temporary is produced and this is bound to the constant reference. You could think of the code as doing g_(std::function<int()>(globalFunctor)). However, this temporary only lives until the end of the constructor, as there is a special rule in C++ saying that temporaries in member initializer lists only live until the end of the constructor. This leaves you with a dangling reference.
The code works when you replace std::function<int(int)> with GlobalFunctor because no conversion is involved. Therefore, no temporaries are produced and the reference directly refers to the global object.
You either need to not use references and store a std::function internally or make a global std::function and have a reference to that.