const class can extend lifetime of const member ref of temporary? - c++

struct A {
// something
~A() { std::cout << "A destruct!\n"; }
};
class refWrapper {
public:
refWrapper(const A& a) : a_(a) {}
~refWrapper() { std::cout << "refWrapper destruct!\n"; }
private:
const A& a_;
};
void func(const refWrapper& ref = A()) {
// why ~A after ~refWrapper
// Rather than after refWrapper constructor complete
}

With default arguments, the call
func();
is equivalent to
func(A()); // so func(refWrapper(A()));
So,
temporary A is created first (destroyed at end of full expression)
temporary refWrapper is created second (bound to parameter reference)
temporary refWrapper destroyed.
temporary A destroyed.
Notice that there is an exception for lifetime extension or parameter:
A temporary object bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
So refWrapper is destroyed at end of full expression, and not at the end of func call (which is the same moment in given example though). So destruction should be done in reverse order of construction.

Related

When will the destructor be called?

#include <memory>
#include <iostream>
class Token { public:
Token() { std::cout << "Token()"; }
~Token() { std::cout << "~Token()"; }
};
template <class T>
std::unique_ptr<T> foo(T t0) {
return std::unique_ptr<T>(new T(t0)); };
int main() {
Token&& t = Token();
auto ptr = foo<Token>(t);
return 0;
}
On which occasions will the destructor be called?
I think it will be called firstly when we call Token(), it creates a temporary object which is destructed immediately, then in the foo() function when t0 is destructed, and then when main() ends, and ptr goes out of scope.
But my friend says otherwise, so how will it actually be?
When a scope ends and the automatic objects of that scope are destroyed, their destructors will be called in the reverse order the objects were created:
int main() {
Token&& t = Token(); // Token1 constructed
// lifetime of Token1 extended because it was bound to t
auto ptr = foo<Token>(t); // creates a copy of Token1 for the argument of foo: Token2
// Token3 constructed by foo in dynamic memory
// and bound to ptr, which
// resides in automatic memory
// Token2 (temporary copy) is automatically destroyed
return 0;
// Last automatic object is destroyed: ptr
// thus, uniqe_ptr destroys Token3
// t is destroyed. This destroys Token1 because
// its lifetime-extending reference
// went out of scope
}
Demo
If you modify your Token class slightly you can observe this live:
class Token {
inline static int C = 1;
int c = C++;
public:
Token(Token const&) : Token() {}
Token(Token&&) : Token() {}
Token() { std::cout << "Token(" << c << ")\n"; }
~Token() { std::cout << "~Token(" << c << ")\n"; }
};
Output:
Token(1)
Token(2)
Token(3)
~Token(2)
~Token(3)
~Token(1)
(live demo)
#bitmask's explanation about life-time is pretty clear. Besides, if you want to get the best performance, maybe you could implement the move constructor for your class Token, and then:
template <class T>
std::unique_ptr<T> foo(T& t0) { // pass-by-reference
return std::make_unique<T>(std::move(t0));
};
As you have used rvalue reference Token&& t to extend the temporary Token object's lifetime in function int main() and the rvalue reference t is a lvalue, so auto foo(T &) will deduce T as class Token and accept t as its parameter t0 according to Overload resolution. Furthermore, std::move cast lvalue t0 to rvalue reference and std::make_unique call the constructor which satisfies std::is_nothrow_constructable_v<Token,Token&&> is true, the move constructor and copy constructor are both candicate functions. If you have implemented Token(Token&&), move constructor will be called. Otherwise is copy constructor.

Side effects of passing temporary as const T& and binding to class reference

Let's say I've a struct definition and some function like this:
struct A{
const std::string& _s;
A(const std::string& s):_s(s){}
//A(const std::string s): _s(s){}
};
int main(void){
A a("E");
std::cout << a._s << '\n';
}
This doesn't error or warn at all by default, you can get it to throw an error at runtime by compiling with asan. From my understanding what happens is, the temporary "E to which s is bound to dies at the end of first expression, meaning A a("E") and therefore the reference in the class gets invalidated after that. So at the cout << point we are accessing a dangling reference.
At compile time, if we replace the first constructor with the 2nd, a warning is generated, that it's binding the reference to the temporary s and so now accessing s after the A a("E") expression is over is also UB.
Assuming the two cases are correct, is there ever a case where
auto x= A("E")._s
would be UB? And if so, when?
The c++17 standard §15.2 point 6.9 says
temporary object bound to a reference parameter in a function call (8.5.1.2) persists until the completion of the full-expression containing the call
So you are good. No UB here auto x= A("E")._s.
The other issues you have described well yourself. After construction accessing the member will be UB.
You can, however, extend the lifetime of a temporary object by binding it to a local const-reference like so:
struct A {
const std::string& _s;
A(const std::string& s) : _s(s) {}
};
int main(void) {
const std::string& e = "E";
A a(e);
std::cout << a._s << '\n';
}
which would not be UB, but I would not use it like this.
Here's some simple testing code which prints exactly when the lifetimes of various objects start and stop:
struct loud {
loud(const char*) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
loud(const loud&) { std::cout << __PRETTY_FUNCTION__ << '\n'; }
~loud() { std::cout << __PRETTY_FUNCTION__ << '\n'; }
};
struct A {
const loud& _s;
// A(const loud& s) : _s(s) {}
A(loud s) : _s(s) {}
};
int main() {
loud x = A("E")._s;
}
Which outputs:
loud::loud(const char *)
loud::loud(const loud &)
loud::~loud()
loud::~loud()
And as you can see, the loud object constructed with "E" is not destroyed before it is copied from.
In both cases, a temporary object is created and is bound to either const loud& s or loud s, the argument of the constructor. All temporary objects are destroyed at the end of the full-expression that contains them, so they will be alive for the entirety of A("E")._s, so can be copied from.
This also means that the following would not work since the temporaries are created in a different full-expression:
// The following is a hard compile time error on clang, but compiles on gcc
struct A {
const loud& _s;
A(loud s) : _s(s) {}
A(const char* s) : _s(s) /* Temporary `loud` used to initialize `_s` is destroyed here */ {}
};
// But this compiles
struct A {
const loud& _s;
A(loud s) : _s(s) {}
A(const char* s) : A(loud(s)) /* Temporary materialized from `loud(s)` is destroyed here */ {}
};
The temporary object is destroyed before the { of the constructor body (and before the constructors of any subsequent data members if there were any), so it is already destroyed before the . in A("E")._s.
In both of these cases, the following is output with the same main function:
loud::loud(const char *)
loud::~loud()
loud::loud(const loud &)
loud::~loud()
(Where the object is used after it is destroyed, which would be UB if the copy constructor tried to access any data members on the object like if it was a std::string)

Return rvalue reference from function

CPP Core guidelines F45 states:
Don't return a T&&.
I want to create some class, and pass the class through a sequence of functions that modify the class. Then I will either evaluate some members of that class, or store the class in some container, like std::vector.
I am trying to do this in a way that the class is constructed exactly once, then destroyed, or moved and the moved-from copy is destroyed, and later the stored copy is destroyed (when the container that I stored it in is destroyed).
In essence, I want to do this:
// some class
class foo{}
// construct a foo, with some parameters, and modify it somehow
auto f1 = modify_foo(foo(x, y, z));
// modify the foo some more
auto f2 = modify_foo(f1);
// modify the foo some more
auto f3 = modify_foo(f2);
// use some element of modified foo
auto v = f3.getx();
// maybe store the modified foo in a vector or some other container
vector<foo> vf;
vf.emplace_back(f3);
It should be possible to construct the foo exactly once, and move the constructed foo through any number of modifying functions, then destroy the foo exactly once.
In the case of storing the foo in a vector, an additional copy/move and destroy will have to occur.
I can achieve this behavior, but I can't figure out any way to do it without using this signature for the modify functions:
foo&& modify_foo(foo&& in);
Here is test code that seems to do what I want:
#include <iostream>
#include <functional>
#include <vector>
// A SIMPLE CLASS WITH INSTRUMENTED CONSTRUCTORS
class foo {
public:
// default constructor
foo() {
std::cout << "default construct\n";
}
// copy constructor
foo(foo const &in) : x{ in.x } {
std::cout << "copy construct\n";
};
// copy assignment
foo& operator=(foo const& in) {
x = in.x;
std::cout << "copy assignment\n";
}
// move constructor
foo(foo&& in) noexcept : x(std::move(in.x)) {
std::cout << "move constructor\n";
}
// move assignment
foo& operator=(foo&& in) noexcept {
x = std::move(in.x);
std::cout << "move assignment\n";
return *this;
}
// destructor
~foo() {
std::cout << "destructor\n";
}
void inc() {
++x;
}
int getx() { return x; };
private:
int x{ 0 };
};
Now a function that will take foo&&, modify foo, return foo&&:
// A SIMPLE FUNCTION THAT TAKES foo&&, modifies something, returns foo&&
foo&& modify(foo&& in) {
in.inc();
return std::move(in);
}
Now using the class and modify function:
int main(){
// construct a foo, modify it, return it as foo&&
auto&& foo1 = modify(foo());
// modify foo some more and return it
auto&& foo2 = modify(std::move(foo1));
// modify foo some more and return it
auto&& foo3 = modify(std::move(foo2));
// do something with the modified foo:
std::cout << foo3.getx();
}
This will do exactly what I want. It will call the constructor once, correctly print 3, and call the destructor once.
If I do the same thing, except add this:
std::vector<foo> fv;
fv.emplace_back(std::move(foo3));
It will add one move construct, and another destruct when the vector goes out of scope.
This is exactly the behavior I want, and I haven't figured out any other way to get there without returning foo&& from the modifier, and using auto&& for my intermediate variables, and using std::move() on the parameters being passed to the subsequent calls to modify.
This pattern is very useful to me. It is bothering me that I can't resolve this with CPP core guidelines F.45. The guideline does say:
Returning an rvalue reference is fine when the reference to the temporary is being passed "downward" to a callee; Then the temporary is guaranteed to outlive the function call...
Maybe that is what I am doing?
My questions:
Is there anything fundamentally wrong, or undefined, in what I am doing?
When I do auto&&, and hover over the foo1, it will show it as a foo&&. I still have to wrap the foo1 with std::move(foo1) to get the modify function to accept it as foo&&. I find this a little strange. What is the reason for requiring this syntax?
As was correctly pointed out by NathanOliver, attempting to use rvalue ref's was leaving a dangling reference to an object that was being destroyed at the end of the function's life.
The piece of the puzzle that I was missing was to use 'auto&', instead of 'auto' when returning a ref from a function:
// function taking lvalue ref, returning lvalue ref
foo& modify(foo& in) {
in.inc();
return in;
}
{
auto f = foo{}; // constructed here
auto f1 = modify(f); // <-- BAD!!! copy construct occurs here.
auto& f2 = modify(f); // <-- BETTER - no copy here
} // destruct, destruct
If I use auto& to capture the lvalue ref returning from 'modify', no copies are made. Then I get my desired behavior. One construct, one destruct.
{
// construct a foo
foo foo1{};
// modify some number of times
auto& foo2 = modify(std::move(foo1));
auto& foo3 = modify(std::move(foo2));
auto& foo4 = modify(std::move(foo3));
std::cout << foo4.getx();
} // 1 destruct here

Can a C++ lambda constructor argument capture the constructed variable?

The following compiles. But is there ever any sort of dangling reference issue?
class Foo {
Foo(std::function<void(int)> fn) { /* etc */ }
}
void f(int i, Foo& foo) { /* stuff with i and foo */ }
Foo foo([&foo](int i){f(i, foo);});
Seems to work. (The real lambda is of course more complicated.)
But is there ever any sort of dangling reference issue?
That depends entirely on what you're doing with Foo. Here's an example that would have dangling reference issues:
struct Foo {
Foo() = default;
Foo(std::function<void(int)> fn) : fn(fn) { }
std::function<void(int)> fn;
}
Foo outer;
{
Foo inner([&inner](int i){f(i, inner);});
outer = inner;
}
outer.fn(42); // still has reference to inner, which has now been destroyed
The lambda expression [&foo](int i){f(i, foo);} will lead compiler to generate a closure class something like this (but not totally correct) :
class _lambda
{
Foo& mFoo; // foo is captured by reference
public:
_lambda(Foo& foo) : mFoo(foo) {}
void operator()(int i) const
{
f(i, mFoo);
}
};
Therefore, the declaration Foo foo([&foo](int i){f(i, foo);}); is treated as Foo foo(_lambda(foo));. Capturing foo itself when constructing does not has problem in this situation because only its address is required here (References are usually implemented via pointers).
The type std::function<void(int)> will internally copy construct this lambda type, which means that Foo's constructor argument fn holds a copy of _lambda object (that holds a reference (i.e., mFoo) to your foo).
These implies that dangling reference issue may arise in some situations, for example:
std::vector<std::function<void(int)>> vfn; // assume vfn live longer than foo
class Foo {
Foo(std::function<void(int)> fn) { vfn.push_back(fn); }
}
void f(int i, Foo& foo) { /* stuff with i and foo */ }
Foo foo([&foo](int i){f(i, foo);});
....
void ff()
{
// assume foo is destroyed already,
vfn.pop_back()(0); // then this passes a dangling reference to f.
}

Temporary lifetime and perfect forwarding constructor

I am having trouble understanding why the lifetime of temporaries bound to const reference parameters is cut short when there is a perfect forwarding constructor around. First of, what we know about temporaries bound to reference parameters: they last for the full expression:
A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call
However I found cases where this is not true (or I might simply misunderstand what a full expression is). Let's take a simple example, first we define an object with verbose constructors and destructors:
struct A {
A(int &&) { cout << "create A" << endl; }
A(A&&) { cout << "move A" << endl; }
~A(){ cout << "kill A" << endl; }
};
And an object wrapper B, which will be used for reference collapsing:
template <class T> struct B {
T value;
B() : value() { cout << "new B" << endl; }
B(const T &__a) : value(__a) { cout << "create B" << endl; }
B(const B &p) = default;
B(B && o) = default;
~B(){ cout << "kill B" << endl; };
};
We can now use our wrapper to capture references on temporaries and use them in function calls, like so:
void foo(B<const A&> a){ cout << "Using A" << endl; }
int main(){ foo( {123} ); }
The program above prints what I would expect:
create A
create B
Using A
kill B
kill A
So far so good. Now let's move back to B and add a perfect forwarding constructor for convertible types:
template <class T> struct B {
/* ... */
template <class U, class = typename enable_if<is_convertible<U, T>::value>::type>
B(U &&v) : value(std::forward<U>(v)) {
cout << "new forward initialized B" << endl;
}
};
Compiling the same code again now gives:
create A
new forward initialized B
kill A
Using A
kill B
Note that our A object was now killed before it was used, which is bad! Why did the lifetime of the temporary not get extended to the full call of foo in this case? Also, there is no other call to the desctructor of A, so there is no other instance of it.
I can see two possible explanations:
either the types are not what I think they are: changing the convertible move constructor to B(T &&v) instead of template <class U>B(U &&v) solves the problem.
or {123} is not a subexpression of foo( {123} ). Swapping {123} for A(123) also solves the issue, which makes me wonder if brace-initializers are full expressions.
Could someone clarify what is going on here?
Does this mean that adding a forwarding constructor to a class could break backward compatibility in some cases, like it did for B?
You can find the full code here, with another test case crashing for references to strings.
The type inferred for U in the call to B<A const&>::B(U&&) is int, so the only temporary that can be lifetime-extended for the call to foo in main is a prvalue int temporary initialized to 123.
The member A const& value is bound to a temporary A, but that A is created in the mem-initializer-list of the constructor B<A const&>::B(U&&) so its lifetime is extended only for the duration of that member initialization [class.temporary]/5:
— A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.
Note that a mem-initializer-list is the part after the colon in a ctor-initializer:
template <class U, class = typename enable_if<is_convertible<U, T>::value>::type>
B(U &&v) : value(std::forward<U>(v)) {
^--- ctor-initializer
^--- reference member
^--- temporary A
This is why kill A is printed after new forward initialized B.
Does this mean that adding a forwarding constructor to a class could break backward compatibility in some cases, like it did for B?
Yes. In this case it's difficult to see why the forwarding constructor would be necessary; it's certainly dangerous where you have a reference member that a temporary could be bound to.
void foo(B<const A&> b);
foo( {123} );
is semantically equivalent to:
B<const A&> b = {123};
that for a non-explicit constructor is semantically equivalent to:
B<const A&> b{123};
going further, since your forwarding-constructor takes anything, it actually is initialized with int, not A:
B<const A&>::B(int&& v)
That is, a temporary instance of A is created on the constructor's initialization list:
B(int&& v) : value(A{v}) {}
// created here ^ ^ destroyed here
which is legal, just like you can type const A& a{123};.
This A instance is destroyed after the B's construction is finished, and you end up with a dangling reference within the body of foo.
The situation changes when you build the instance in a call expression, then the A temporary ends its lifetime at the end of the call expression:
foo( A{123} );
// ^ A is destroyed here
so it stays alive within foo, and the forwarding-constructor selected for B<const A&> is instantiated with a type A&&.