In C++ it is valid to use take a const reference to a temporary:
const std::string& s = std::string("abc");
std::cout << s.length() << std::endl; // valid because string instance is still alive
But does this hold true if the temporary was created via a conversion from another type?
For example:
struct Foo
{
~Foo()
{
cout << "Foo destructor?" << endl;
}
};
struct Bar
{
operator Foo()
{
return Foo();
}
~Bar()
{
cout << "Destructor" << endl;
}
};
Foo getFoo()
{
return Foo();
}
Bar getBar()
{
return Bar();
}
int main()
{
const Foo& f = getBar();
/* is f valid here, or is it a dangling reference? */
std::cout << "We're still in main!" << std::endl;
}
I note that Bar's destructor is called before We're still in main is output, which makes me think that Foo& f is a dangling reference. Am I correct?
It does not matter how the temporary was created. If you bind a const X& or an X&& to a local prvalue, the lifetime of the temporary gets extended to the lifetime of the reference.
The function getBar creates an object of type Bar and immediately destroys returning a copy of it.
Bar getBar()
{
return Bar();//the lifetime of Bar() is only on this line;
}
Edit:
For the question in the source code if const Foo & f is valid; yes it is because getBar returns an object copy.
Also after checking the code i see that it first returns a copy of Bar and then casts it to Foo
Also i must mention the RVO (from the comments section) which is an optimization from the compiler. The lifetime of the object is still defined by it's scope {} however in this case the construction is done inside the function and the destruction is outside the function. This optimization will not work of you give a name to the variable like such:
Bar getBar()
{
Bar tmp_value;
return tmp_value;
}
Razvan.
Related
Example:
Foo return_a_foo()
{
const auto a_foo = make_a_foo();
//Work with, but do not mutate a_foo...
return a_foo;
}
If the compiler cannot employ RVO, I'd at least expect it to try and move a_foo. However, a_foo is const (but still about to go out of scope). Does it say anywhere in the standard that this is 100% guaranteed not to move (bummer) or is it implementation defined ?
Can a const object, returned by value, still be moved?
Some will be shocked to learn that the answer is, "sometimes, yes".
However, you have to give them more constructors in order to enable this. You would also have to either make members mutable or otherwise manually handle const-move-construction.
Proof:
#include <iostream>
#include <memory>
struct Foo
{
Foo() { std::cout << "default c'tor\n"; }
Foo(Foo const&&) { std::cout << "Foo const&&\n"; }
Foo(Foo &&) { std::cout << "Foo &&\n"; }
Foo(Foo const&) { std::cout << "Foo const&\n"; }
Foo(Foo &) { std::cout << "Foo &\n"; }
};
const Foo make_a_foo()
{
auto p = std::make_unique<const Foo>();
return std::move(*p);
}
const Foo return_a_foo()
{
const auto a_foo = make_a_foo();
//Work with, but do not mutate a_foo...
return a_foo;
}
int main()
{
auto f = return_a_foo();
}
example output:
default c'tor
Foo const&&
No, a const T can't be bound to a non-const T&&, so a_foo can't be moved from.
Your function returns a non-const Foo though, so a_foo will be copied to the return value. The return value can then be moved from, since it's non-const. See this example.
In reality all of those copies and moves will likely be elided.
Can someone help me to understand why the following code causes an error?
class A
{
public:
float& operator()()
{
return _f;
}
private:
float _f = 1;
} a;
auto& foo()
{
std::function<float()> func = a;
return func();
}
int main()
{
std::cout << foo() << std::endl;
}
Error:
error: non-const lvalue reference to type 'float' cannot bind to a temporary of type 'float'
return func();
^~~~~~
1 error generated.
Here, in operator(), I return a reference to _fand consequently, I thought func() is not a temporary.
It would be great if someone helps me understand.
The problem isn't the use of std::function, its that you're trying to return the temporary float from func() as a reference.
This won't work since the object would cease to exist as soon as the statement ends.
If you change auto& foo() to auto foo() it should work.
I think you understand that returning a reference to a local variable isn't valid once the variable goes out of scope. What you seem to be missing though is that std::function<float()> func = a; actually creates a local std::function from a. It doesn't point to a in any way, func has it's own A. Which means that calling func(); doesn't actually invoke a.operator() but rather the A of func. Then we get back to the local variable returning a reference is evil part.
To make it compile, you can change your template signature to float&() but it's still undefined behaviour.
A fix would be to change the return type to a copy instead (to auto), removing the reference.
For std::function<float()> func, you're declaring func as a functor returning a float, not a float&. As the error message said, the temporary float returned by func() can't be bound to non-const lvalue reference.
The above declaration doesn't match the signature of A::operator() which being wrapped. But note that if change the type to std::function<float&()> func to match the signature of A::operator(), the compile error could be sovled, but then we'll return a reference bound to local variable, which leads to UB.
Note that for std::function<float()> func = a;, std::function is initialized with a copy of a. Then func() will return a reference bound to member of A wrapped in func, which is a local variable. And the reference will dangle when get out of function foo.
How to fix it depends on your design, change auto& foo() to auto foo(), i.e. passing the return value by copy would avoid UB here.
After reading the great answers above, I tried to give some different thoughts.
I guess OP really wants to return a float& of a certain object (which is a in OP's example).
So if OP wants foo to return auto& (which should be a float&), then it should be as the following, please note the std::bind part:
namespace T1
{
class A
{
public:
float& operator()()
{
std::cout << "a add = " << this << std::endl;
return _f;
}
float getF() { return _f; }
private:
float _f = 1;
} a;
auto& foo()
{
std::function<float&()> func = std::bind(&A::operator(), &a);
return func();
}
} // end of namespace T1
int main()
{
std::cout << "global a add = " << &(T1::a) << std::endl; // check a's address
float& f = T1::foo(); // note that `a`'s address is the same
std::cout << f << std::endl; // still 1
f = 777;
std::cout << f << std::endl; // now 777
std::cout << T1::a.getF() << std::endl; // it's 777
return 0;
}
I have the following code, was wondering when Foo's destructor is called.
#include <iostream>
class Foo {
public:
Foo() {
}
~Foo() {
std::cout << "destruct" << std::endl;
}
};
void go(Foo f) {
std::cout << "go" << std::endl;
}
int main() {
go(Foo());
std::cout << "main" << std::endl;
return 0;
}
If I run the code, I got the following output
go
destruct
main
It shows Foo's destructor is called after go is done. My gcc is 4.8.3.
I had thought the temporary Foo's object should be deleted after it is copied to go's argument. But this is not the case, and only one object of Foo exists. Is this expected or undefined in terms of compiler's implementation?
It's an optimization permitted by the C++ Standard.
The C++ standard draft, [class.temp/2] says and I quote (relevant parts only; emphasis are mine):
The materialization of a temporary object is generally delayed as long
as possible in order to avoid creating unnecessary temporary objects.
.....
Example:
class X {
public:
X(int);
X(const X&);
X& operator=(const X&);
~X();
};
class Y {
public:
Y(int);
Y(Y&&);
~Y();
};
X f(X);
Y g(Y);
void h() {
X a(1);
X b = f(X(2));
Y c = g(Y(3));
a = f(a);
}
X(2) is constructed in the space used to hold f()'s argument and
Y(3) is constructed in the space used to hold g()'s argument.
Formerly, in n3690, it said:
An implementation might use a temporary in which to construct X(2)
before passing it to f() using X’s copy constructor;
alternatively, X(2) might be constructed in the space used to hold
the argument
That means, this:
void go(Foo) {
std::cout << "go" << std::endl;
}
int main() {
go(Foo());
}
is sometimes as "performant" as you want!, See, C++ is gradually getting there ;-).
But you see, using std::move will inhibit that behavior, because std::move produces an xvalue expression from a materialized object:
void go(Foo) {
std::cout << "go" << std::endl;
}
int main() {
go(std::move(Foo()));
}
In conclusion,
When not using std::move in this your case, the object is created once as seen Live on Coliru
But when you use std::move, it is created twice as seen Live on Coliru, this is because of materialization of the object. Read the complete paragraph of class.temp/2 to understand what materialization means.
I wanted to restrict a specific class to be creatable on the stack only (not via allocation). The reason for this is that on the stack, the object which lifetime has begun last, will be the first to be destroyed, and I can create a hierarchy. I did it like this:
#include <cstddef>
#include <iostream>
class Foo {
public:
static Foo createOnStack() {
return {};
}
~Foo () {
std::cout << "Destructed " << --i << std::endl;
}
protected:
static int i;
Foo () {
std::cout << "Created " << i++ << std::endl;
}
Foo (const Foo &) = delete;
};
int Foo::i = 0;
The constructor normally should push the hierarchy stack, and the destructor pops it. I replaced it here for proof of concept. Now, the only way you can use such an object is by storing it in a temporary reference like this:
int main() {
Foo && a = Foo::createOnStack();
const Foo& b = Foo::createOnStack();
return 0;
}
My question now is, how safe is this with the C++ standard? Is there still a way to legally create a Foo on the heap or hand it down from your function into another frame (aka return it from your function) without running into undefined behaviour?
EDIT: link to example https://ideone.com/M0I1NI
Leaving aside the protected backdoor, C++17 copy elision breaks this in two ways:
#include<iostream>
#include<memory>
struct S {
static S make() {return {};}
S(const S&)=delete;
~S() {std::cout << '-' << this << std::endl;}
private:
S() {std::cout << '+' << this << std::endl;}
};
S reorder() {
S &&local=S::make();
return S::make();
}
int main() {
auto p=new S(S::make()),q=new S(S::make()); // #1
delete p; delete q;
reorder(); // #2
}
The use of new is obvious and has been discussed.
C++17 also allows prvalues to propagate through stack frames, which means that a local can get created before a return value and get destroyed while that return value is alive.
Note that the second case already existed (formally in C++14 and informally long before) in the case where local is of type S but the return value is some other (movable) type. You can't assume in general that even automatic object lifetimes nest properly.
So from a question asked in another thread, I have thought of a new question and the answer is not obvious to me.
So it appears there is a c++ rule that says if you have a const reference to a temporary, then the lifetime of the temporary is at least as long as the const reference. But what if you have a local const reference to another object's member variable and then when you leave scope - Does it call the destructor of that variable?
So here is modified program from the original question:
#include <iostream>
#include <string>
using namespace std;
class A {
public:
A(std::string l) { k = l; };
std::string get() const { return k; };
std::string k;
};
class B {
public:
B(A a) : a(a) {}
void b() { cout << a.get(); } //Has a member function
A a;
};
void f(const A& a)
{ //Gets a reference to the member function creates a const reference
stores it and goes out of scope
const A& temp = a;
cout << "Within f(): " << temp.k << "\n";
}
int main() {
B b(A("hey"));
cout << "Before f(): " << b.a<< "\n";
f(b.a);
cout << "After f(): " << b.a.k << "\n";
return 0;
}
So when I run this code, I get "hey" as the value everytime. Which seems to imply that a local const reference does not bind itself through life with a passed in member object. Why doesn't it?
b.a is not a temporary so its lifetime is not affected by any references that are subsequently bound to it.
I'm not sure I understand what you're asking. In your code, the only
temporary I see is the A("hey") in the expression that initializes b
in main. And that is copied (using the copy constructor) into b.a
in B::B. After that, there are no more temporaries, anywhere.
More generally, the fact that a temporary is bound to a reference
doesn't necessarily change its lifetime. What extends the lifetime is
the fact that the temporary is used to initialize the reference: in your
case, for example, temp in f will never have an effect on the
lifetime of a temporary, because it is not initialized with a temporary,
but with another reference. And there are exceptions to this rule: if
you use a temporary to initialize a member reference in the initializers
of a class, it's lifetime will still not extend beyond the end of the
constructor, so:
class A
{
std::string const& rString;
public:
A() : rString( std::string( "hey" ) ) {}
std::string get() const { retur rString; }
};
will not work.