This is a very quick question. I have the following code:
struct integer {
int x;
int& ref() {
return x;
}
};
//main...
int* l = &integer{4}.ref();
std::cout << *l;
My question is: Isn't &integer{4}.ref() a rvalue since it is a temporary object? How can I have a pointer to it? And is it undefined behavior?
While integer{4} is an rvalue, you call ref() which returns an int& which is always an lvalue. This allows the code to compile, but you have undefined behavior since you dereference a pointer to an object that no longer exists.
To fix this you can provide a ref-qualifer for ref which only allows it to be called on lvalues. That would look like
struct integer {
int x;
int& ref() & {
return x;
}
};
and will cause a compiler error if you try to use
integer{4}.ref()
since integer{4} is not an lvalue which ref now requires.
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
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.
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.
I was reading C++ Faq Second Edition , faq number 32.08 .
FAQ says that parameter passed by const reference and returned by const reference can cause dangling reference.
But it is ok if parameter is passed by reference and returned by reference.
I got it that it is unsafe in case of const reference but how is it safe in case when parameter is non const reference.
Last line of FAQ says
"Note that if a function accepts a parameter by non-const reference (for example, f(string& s)), returning a copy of this reference parameter is safe because a temporary cannot be passed by non-const reference."
Need some insight on this!!
if you have like
const Foo & bar(const Foo &f) { return f; }
and call it like
const Foo &ret = bar(Foo());
This compiles, but the problem is that now 'ret' is a dangling reference, because the temporary object created by the call to Foo() gets freed after bar returns. The detailed execution sequence here is:
temporary Foo is allocated
bar is called with a reference to the temporary object
bar returns the reference
now that bar has returned the temporary Foo is released
the reference is now dangling as the object was destroyed
However, if you had Foo declared as
Foo & bar(Foo &f) { return f; }
then your call bar(Foo()) would not be accepted by compiler. When you pass a temporary object to a function, you can only take it by const reference or as a copy; this is part of the language definition.
Temporaries can be passed by const reference - when the function returns the temporaries are destoyed, so the caller is left with a dangling ref.
For example:
#include <iostream>
using namespace std;
int const& output( int const& x)
{
cout << x << endl;
return x;
}
int main ()
{
int a = 1;
int const& ref1 = output( a); // OK
int const& ref2 = output(a+1); // bad
return 0;
}
I think this example will be helpful:
const int& f(const int& n)
{
return n;
}
int f1(int& n)
{
return n;
}
int main(int argc, char **argv)
{
//Passing a reference to an anonymous object created by
//the compiler to function f()
const int& n = f(10);
//Undefined behavior here as the scope of the anonymous object
//was only till previous statement
int k = n + 10;
//Compiler error - Can not pass non-const reference to a anonymous object
int n = f1(10);
}
Here is a page about C++0x rvalue references that starts with a pretty decent summary of how lvalues and rvalues work in C++, along with how they are allowed to bind with references.
Most articles you will find about rvalue references in C++0x will give you some insight into this.