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.
Related
For the following C++ codes:
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A()\n"; }
~A() { cout << "~A()\n"; }
A* get() { return this; } // <------
};
void f(const A * const &a, const A * const &b) {
cout << "f()\n";
}
int main() {
// f(&A(), &A()); // error: taking address of temporary
f(A().get(), A().get()); // <------
cout << "end\n";
return 0;
}
And it returns:
A()
A()
f()
~A()
~A()
end
Of course I can't use &A() here because this leads to an error: error: taking address of temporary.
But it works when I wrapped it in A* get() { return this; }.
So my question is, is it an Undefined Behaviour to use get()?
So my question is, is it an Undefined Behaviour to use get()?
No, it's not undefined behavior. It's valid because the temporary object is first constructed, then the function is called, and then (only after the function returns) is the temporary object destroyed again. So during the lifetime of the function call, you're allowed to do anything you could normally do with the passed-in temporary object, including dereferencing a pointer to it or its contents. (your function shouldn't store the passed-in pointer for later use, though, since it will become a dangling-pointer as soon as the function returns!)
I'm trying to write a class that contains a function returning one of the class members, and I want to allow the caller to either move or copy the returned value. I wrote some dummy structs to test this; and after trying different variations, this seems to give me what I want.
#include <iostream>
using namespace std;
struct S {
int x;
S() : x(10) { cout << "ctor called\n"; }
S(const S& s) : x(s.x) { cout << "copy ctor called\n"; }
S(S&& s) : x(s.x) { cout << "move ctor called\n"; }
// I'm implementing move and copy the same way since x is an int.
// I just want to know which one gets called.
};
struct T {
S s;
T() : s() {}
S&& Test() && {
return move(s);
}
const S& Test() & {
return s;
}
};
int main() {
T t;
auto v = move(t).Test();
cout << v.x << "\n";
T t2;
auto w = t2.Test();
cout << w.x << "\n";
return 0;
}
The code prints out (with clang++-5.0 c++14):
ctor called
move ctor called
10
ctor called
copy ctor called
10
Is this an acceptable way to implement what I want? I have a few questions:
In the first Test function, I tried both S&& and S for the return type and it doesn't change the output. Does && mean anything for the (non-template) returned type?
Is it guaranteed that auto v = move(t).Test() would only invalidate the "moved" member? If struct T had other member variables, can I assume this call wouldn't invalidate them?
In the first Test function, I tried both S&& and S for the return type and it doesn't change the output. Does && mean anything for the (non-template) returned type?
There are little differences:
S&& is a (r-value) reference, so object is not yet moved.
returning S would move-construct S, so member is moved once the method is called.
For move(t).Test();, return ingS&& does nothing whereas returning S would move the member.
Is it guaranteed that auto v = move(t).Test() would only invalidate the "moved" member? If struct T had other member variables, can I assume this call wouldn't invalidate them?
Yes, only T::s is moved. std::move is just a cast to rvalue.
Yes it is acceptable way to implement this.
It does the same thing because returned value is temporary object, thus rvalue.
Depends on what you mean by invalidating
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.
I am compiling with g++ 4.8.4 under Ubuntu.
I cannot understand why the code below is working correctly means that prints always on console some output without crashing.
My believe is that the function foo() is assigned a temporary object that will last until the function foo() finishes its execution.
Surely the output argument will point to the same address on the stack where that temporary has been allocated, but I was surprised to find that each call to A::hello() works fine.
I thought that any access to that area of memory should be avoided.
I wanted to double check with 'valgrind' and it is also saying that all is ok. I tried to recompile with -Wstack-protector and nothing.
Do you know why it is happening? Is my believe wrong or it is simply one of those 'undefined' C++ behaviour that is best to avoid?
#include <iostream>
using namespace std;
struct A {
A(): a(10) { cout << "a" << endl; }
~A() {cout << "bye" << endl; }
void hello() const { cout << "hi " << a << endl; }
};
const A& foo(const A& a = A()) {
return a;
}
int main() {
for( int i = 0; i < 10 ; i++) {
const A& a = foo();
a.hello();
}
return 0;
}
Output
'a'
'bye'
'hi 10'
'a'
'bye'
'hi 10'
...
The behaviour is undefined.
Binding a const reference to an anonymous temporary extends the lifetime of that anonymous temporary to the lifetime of that const reference.
But the attempted re-binding of the returned reference to a in foo will not extend the lifetime: lifetime extension is not transitive. So a is a dangling reference in main().
I am trying to wrap my head about scope in C++. Please consider the following:
class C
{
int i_;
public:
C() { i_ = 0;}
C(int i) { i_ = i; }
C(const C &c) {
i_ = c.i_;
cout << "C is being copied: " << i_ << endl;
}
int getI() { return i_; }
~C() {cout << "dstr: " << i_ << endl;}
};
class D
{
C c_;
public:
void setC(C &c) { c_ = c; }
int getC_I() { return c_.getI(); }
};
void Test(D &d)
{
C c(1);
d.setC(c);
//here c is going out of scope, surely it will be destroyed now?
}
int main()
{
D d;
Test(d); //this sets value of c_ to the local variable in Test.
//Surely this will be invalid when Test returns?
int ii = d.getC_I();
cout << ii << endl;
}
Running this program outputs:
dstr: 1
1
dstr: 1
Apparently, the first destructor call occurs in Test, and the other when program terminates and d is destroyed.
So my question is: Where was c copied? Is there fault with my reasoning? And general point I am trying to ask: is it safe to have a member function that takes a reference to an object and then stores it in a member variable?
Many thank for your help.
Your code is fine as it stands right now. D::c_ is of type C rather than C &. Your SetC takes a reference to a C, and assigns the value referred to by that reference to C::c_, so what you have is an entirely separate C object that has the same value. Since you created d with automatic storage duration in main, it and c_ which is part of it remain valid until you exit from main.
Where was c copied?
When you do c_ = c;Where was c copied?, you're calling operator= on c_ which will copy the contents of c to c_.
If c_ were a also reference, nothing would be copied, and the program would cause an error like you expect it to.
C is getting copied here:
void setC(C &c) { c_ = c; }
If you want to store a reference then your member variable c_ should also be a reference. If you are storing a reference then you'll have to be careful with the lifetime of the variable you're passing in.