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.
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.
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.
I had a Q&A before: Point of declaration in C++. The rule point-of-declaration nicely is applicable on many situations. Now, I confused on usage of auto in combination of this rule.
Consider these two codes:
i. Declaring x by itself (we don't expect it to work):
{
auto x = x;
}
ii. Declaring the inner x by the outer x (It makes error in gcc 4.8.x):
{
int x = 101; // the outer x
{
auto x = x; // the inner x
}
}
According to the rule of point-of-declaration, it should work but it doesn't. It seems there is another rule in the standard that I missed it. The question is, Where is the point-of-declaration when using auto?
There are two possibilities:
i. If the point of declaration is after =, at the end of statement:
auto object = expression;
^
Is it here? If it is, why gcc complains?
So the second declaration is valid and must work, because there is no x but that outer one (which is declared before). Therefore auto x=x is valid and the inner x should be assigned to 101.
ii. If the point of declaration is before = :
auto object = expression;
^
Well, it doesn't make any sense because auto has to wait until see the following expression. For example auto x; is invalid.
Update: I need an answer which explains it by the rule point of declaration.
auto x = x; // inner x
is ill-formed.
To quote from the C++11 standard (emphasis mine):
7.1.6.4 auto specifier
...
3 Otherwise, the type of the variable is deduced from its initializer. The name of the variable being declared
shall not appear in the initializer expression. ...
And so because x after = resolves to the x in auto x (as explained in the question you linked), that above piece of code is ill-formed.
Just like in any other kind of definition, the x on the right-hand side of the initialiser for auto x = x resolves to the local auto x. C++ has always done this (i.e. int x = x compiles but will give you undefined behaviour).
The reason auto x = x fails to compile is because while x is in scope it has no known type yet, and so using it as the initialiser fails because the type can't be deduced from the expression.
Just like any other kind of declaration, x is in scope after its declarator which is auto x.
int x = 10;
int y = 20;
{
int x = x; // This is NOT the outer x. This is undefined behaviour (reading an
// uninitialised variable).
auto y = y; // This is NOT the outer y. This is a compile error because the type of
// y is not known.
}
Just adding an example with more explicit diagnostics:
auto ll = [&] { ll(); };
Results in (gcc):
error: variable ‘auto ll’ with ‘auto’ type used in its own initializer
or (clang):
error: variable 'll' declared with 'auto' type cannot appear in its own initializer
auto ll = [&] { ll(); };
^
You can see that there is an explicit rule for this. I haven't looked at the specs.
The compiler reads a whole statement (from the beginning of a line until the next semi-colon) and then evaluates the different parts of a statement using priorities of operations, and then when the time comes when the value of auto x is to be assigned, the type that came up after th = sign gets taken.
For example:
template <typename T>
T sum(T a, T b)
{
return a+b;
}
int main()
{
auto x = sum<double>(1,5); // x here is a double, because the return value is double
auto y = sum<int>(1,7); //y is an int, because the return value is int
}
And about your auto x = x, you're redefining the same variable name. That's invalid! auto y = x shall work.
template<class U>
void f( U && v)
{
std::cout << typeid(v).name() << "\n"; //'int' in both cases
if( boost::is_same<int&&,U>::value )
{
std::cout << "reach here\n"; //only with f<int&&>(int(1));
}
}
int main()
{
f(int(1));
f<int&&>(int(1));
std::cin.ignore();
}
Why v parameter is interpreted as int when I don't explicitly use f<int&&>?
What is the difference ? (Compiled with MVS2010)
My guess is that First is passed as a rvalue and second as a rvalue reference and both bound correctly into a rvalue reference, am I right ?
Thanks.
No, not really. An rvalue reference is never deduced. The notion U&& with U being a deducible template parameter is used to indicate that U should be deduced such that the rvalue-ness of the argument is retained:
when passing an rvalue of type X the type of U becomes X.
when passing a cv qualified lvalue of type X then U becomes the type X cv&.
The more interesting question is what happened to the rvalue references explicitly specified in the second call because there is no deduction going on because in this case the two rvalue references are collapsed into just one.
First variant
f(int(1)) <=> f<int>(int(1)) <=> U==int <=> is_same<int&&,int> == false
Second variant
f<int&&>(int(1)) <=> U==int&& is_same<int&&,int&&> == true
Like this