#include <iostream>
int foo()
{
return 0;
}
int main()
{
const int& a = foo();
std::cout << &a << std::endl;
}
In this code, a binds to a rvalue. Is it legal to take its address? (And by legal I mean: in the code ill-formed? Am I causing an undefined behaviour?)
This is fine. In C++11 you can even do this:
int&& a = foo();
a = 123;
You can kind of think about temporaries like this (conceptually and in general):
x = func(); // translated as:
auto __temporary = func();
x = __temporary;
__destruct_value_now_and_not_later(__temporary);
Except if x is the definition of a reference type, the compiler notes that you're purposefully referring to the temporary value and extends its lifetime by removing the early destruction code, making it a normal variable.
Yes. Until the variable a goes out of scope, the temporary it captures is valid. Herb Sutter can explain it better.
Related
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.
Why does this code compile with GCC (4.9 and 5+), but not with clang (3.5-3.9)?
void test(const int&) { }
int main() {
const int x = 42;
auto f = []{ test(x); };
}
I have some vague idea that the discrepancy has to do with ODR (One Definition Rule) usage, but I don't understand that well enough to figure out what's going on here.
x is odr-used because it's bound to a reference (test's parameter). It therefore must be captured ([expr.prim.lambda]/13):
If a lambda-expression or an instantiation of the function call
operator template of a generic lambda odr-uses ([basic.def.odr]) this
or a variable with automatic storage duration from its reaching scope,
that entity shall be captured by the lambda-expression.
Violations of this rule, like all other rules in the standard that doesn't say "no diagnostic required" or "undefined behavior", require a diagnostic.
GCC, unfortunately, performs constant folding too early, before it could tell whether it's an odr-use or not. This can lead to problems such as [&]()->const int & { return x; } returning a dangling reference.
T.C. has the right diagnosis, here's a clearer bit of legal code where clang does the right thing and gcc doesn't:
#include <iostream>
void test(const int&a) { std::cout << "in test() -- " << &a << "\n"; }
int main() {
const int x = 42;
std::cout << "in main() -- " << &x << "\n";
auto f = [&]{ test(x); };
f();
}
gcc prints different addresses for a capture-by-reference variable than the original!
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 was reading a blog about c++11 rvalue references by Thomas Becker, and the following syntax near the bottom of the page really confuses me.
int& foo();
foo() = 42; // ok, foo() is an lvalue
What exactly is foo? A function pointer that returns a int; an object?
If a function object, how can you assign a value to it, and why is foo a lvalue?
int& foo(); declares a function foo that takes no arguments and returns a reference to an int. For example,
#include <iostream>
int a = 0;
int& foo();
int main()
{
std::cout << a << std::endl;
foo() = 42;
std::cout << a << std::endl;
}
int& foo() { return a; }
Output:
0
42
foo is a function with return type int&. The line
int& foo();
simply declares the function, but does not define it.
When foo is called, the result is an lvalue of type int. Therefore you can assign to it: foo() = 42;
Every function has at least one return expression. Let's look at function calling using the following incomplete, simplified description :
Assume your function signature is T f();, with one return expression return e;. For the sake simplicity, lets also assume that e and T have the same type after removing references and const-ness. In this situation, you can go to your function call location and replace the call f() with (T)e, to understand what's going on.
int& f(){....; return e;}
.
.
.
f() = 5;
becomes
(int&)e = 5;
Of course, if converting e to an lvalue reference(int&) was invalid, the compiler will throw an error.
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.