C++ bind reference member to constructor parameter - c++

When running the following code on gcc 8 (https://wandbox.org/, with "g++ prog.cc -Wall -Wextra -std=c++1z"):
#include <iostream>
class B{
public:
B(): copy(false){ std::cout << "B-constructed" << std::endl;}
B(const B& b): copy(true){ std::cout << "B-copy-constructed" << std::endl; }
~B(){ std::cout << (copy?"B-destructed":"B-(copy)-destructed") << std::endl;}
bool copy;
};
class A{
public:
A(B b): bref(b){std::cout << "A-constructed" << std::endl;}
~A() {std::cout << "A-destructed" << std::endl;}
B &bref;
};
void f(){
B b;
A a(b);
std::cout << "f over" << std::endl;
}
int main()
{
f();
std::cout << "main over" << std::endl;
return 0;
}
the following output is yielded:
B-constructed
B-copy-constructed
A-constructed
B-destructed
f over
A-destructed
B-(copy)-destructed
main over
The order of object destructions seems unusual. It is as if the lifetime of the constructor's parameter is extended. Does the standard say anything about binding member references to constructor parameters?
I don't think that this quote from the standard applies, as the parameter is not a temporary object (however I do not know the definition of a "temporary expression"):
A temporary expression bound to a reference member in a mem-initializer is ill-formed. [ Example:
struct A {
A() : v(42) { } // error
const int& v;
};
—end example ]

Your destructor has a logical error, since you print that a copy is destructed when copy is wrong.
Change this:
~B(){ std::cout << (copy?"B-destructed":"B-(copy)-destructed") << std::endl;}
to this:
~B(){ std::cout << (copy?"B-(copy)-destructed":"B-destructed") << std::endl;}
which now outputs:
B-constructed
B-copy-constructed
A-constructed
B-(copy)-destructed
f over
A-destructed
B-destructed
main over
nice and clear (Order of member constructor and destructor calls).
Does the standard say anything about binding member references to constructor parameters?
Similarly, before the lifetime of an object has started but after the
storage which the object will occupy has been allocated or, after the
lifetime of an object has ended and before the storage which the
object occupied is reused or released, any glvalue that refers to the
original object may be used but only in limited ways. For an object
under construction or destruction, see [class.cdtor]. Otherwise, such
a glvalue refers to allocated storage
([basic.stc.dynamic.deallocation]), and using the properties of the
glvalue that do not depend on its value is well-defined.
Source

Related

When does reference casting slice objects?

Take a look at this piece of code:
#include <iostream>
class A{
public:
int x;
virtual void f(){std::cout << "A f\n";}
};
class B: public A
{
public:
int y;
void f() {std::cout << "B f\n";}
};
void fun( A & arg)
{
std::cout << "fun A called" << std::endl;
arg.f();
// arg.y = 222; - this gives error, compiler's work?
arg.x = 2223333;
}
void fun(B & arg){
std::cout << "fun B called" << std::endl;
arg.f();
}
int main()
{
B b;
b.y = 12;
b.x = 32;
fun(static_cast<A&>(b));
std::cout << b.x << " " << b.y << std::endl;
return 0;
}
What exactly happens when I reference cast b into A&? I'm guessing a reference to type A 'arg' is created in a funtion 'fun()' and now it's only compiler's work to differentiate types? Meaning no actual object was created and no slicing occurred and it's still the same object in memory, however compiler will treat it as type A? (meaning after function call I can safely use b as type B?)
I assumed that's true, because the vptr of the instance didn't change (arg of type A called B's virtual function override), but I'm not completely sure what's going on behind the scenes during reference casting.
Also, if I assign static_cast<A&>(b) to a new object of type A, I assume that's when the construction of a new object of type A and slicing occurres?
Yes, you seem to have got this. :-)
A B is also an A (by inheritance), so it can bind to either A& or B&. Nothing else happens, it is just a reference to the existing object.
The slicing happens if you assign a B object to an A object, like A a = b;, which will only copy the inherited A portion of b.

Why unique_ptr doesn't destroy object immediately when goes out-of-scope?

I have the following piece of code:
struct C
{
C() {std::cout << ">>> constructor\n"; }
~C() {std::cout << ">>> destructor\n"; }
};
void bar(std::unique_ptr<C>&& p)
{
std::cout << "bar\n";
}
int main()
{
auto instance = std::make_unique<C>();
std::cout << "after construction\n";
bar(std::move(instance)); // #1
std::cout << "after bar\n";
return 0;
}
On line #1, I'm moving the pointer to the function bar. I was thinking that in this call the pointer-to-C is moved-out from unique_ptr instance and passed to the argument unique_ptr p. If so, then the C destructor should be called at the end of bar()'s execution when argument p is destructed. But it turned out that the instance of C is alive until the end of main()' s execution. The output looks as follows:
>>> constructor
after construction
bar
after bar
>>> destructor
Why does it happen? How to interpret this behavior?
std::move doesn't actually move. It just casts it into a an rvalue reference.
Change
void bar(std::unique_ptr<C>&& p)
to
void bar(std::unique_ptr<C> p)
To actually do the move in the move constructor.

C++ - Destructor call order

I have the following C++ code:
#include <iostream>
struct A
{
A() { std::cout << "A" << ++x; }
A(int x) : A() { std::cout << x; }
~A() { std::cout << "D"; }
static int x;
};
int A::x = 0;
struct B
{
A a, aa, aaa;
B() : aa(1), a(2) { std::cout << "B" << std::endl; }
~B() { std::cout << "B" << A::x; }
};
B beta;
int main()
{
return 0;
}
I understand everything in the control flow except of destructor calls.
Here is the control flow without destructors:
create object B
call constructor B
call a,aa,aaa respectively
2.1 for a, call A(int x)
2.2 for aa, call A(int x)
2.3 for aaa, call A()
display B from B c-tor body
Now the 4. step is to call destructor B, I know that.
What I don't know is what is the order of calling destructors for A.
Is it a,aa,aaa respectively, or aaa,aa,a respectively?
Thanks in advance.
The member objects get destroyed in the reversed order they got constructed. Note that you do not influence this order by changing the order in the constructor's initialization list. The order is exclusively determined by the order you declare them in the struct/class definition.
What I don't know is what is the order of calling destructors for A. Is it a,aa,aaa respectively, or aaa,aa,a respectively?
Thus, the latter case is happening.
Everything looks fine. It constructs-destructs stack-wise (First in/Last out):
#include <iostream>
struct A
{
A() { name="untitled"; std::cout << name <<" constructor" << std::endl; }
A(std::string name):name(name) { std::cout << name <<" constructor" << std::endl; }
~A() { std::cout << name <<" destructor" << std::endl; }
std::string name;
};
struct B
{
A a, aa, aaa;
B() : aa("aa"), a("a") { std::cout << "B constructor" << std::endl; }
~B() { std::cout << "B destructor" << std::endl; }
};
B beta;
int main()
{
return 0;
}
Result:
a constructor
aa constructor
untitled constructor
B constructor
B destructor
untitled destructor
aa destructor
a destructor
Is this order guaranteed? yes
If you turn on all the warnings you see this:
g++ -Wall -Wreorder main.cpp
main.cpp: In constructor ‘B::B()’:
main.cpp:12:10: warning: ‘B::aa’ will be initialized after [-Wreorder]
A a, aa, aaa;
^
main.cpp:12:7: warning: ‘A B::a’ [-Wreorder]
A a, aa, aaa;
^
main.cpp:13:5: warning: when initialized here [-Wreorder]
B() : aa("aa"), a("a") { std::cout << "B constructor" << std::endl; }
^
This took forever to find, but per n4659 (ISO C++17 draft):
15.6.2 Initializing bases and members
paragraph (13.3)
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the
reverse order of initialization. — end note ]
Here, mem-initializers are the list following the colon in the constructor definition.

The life range of a temporary object created at callsite arguments

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.

C++11 lambdas: member variable capture gotcha

Consider this code:
#include <memory>
#include <iostream>
class A
{
public:
A(int data) : data_(data)
{ std::cout << "A(" << data_ << ")" << std::endl; }
~A() { std::cout << "~A()" << std::endl; }
void a() { std::cout << data_ << std::endl; }
private:
int data_;
};
class B
{
public:
B(): a_(new A(13)) { std::cout << "B()" << std::endl; }
~B() { std::cout << "~B()" << std::endl; }
std::function<void()> getf()
{
return [=]() { a_->a(); };
}
private:
std::shared_ptr<A> a_;
};
int main()
{
std::function<void()> f;
{
B b;
f = b.getf();
}
f();
return 0;
}
Here it looks like I'm capturing a_ shared pointer by value, but when I run it on Linux (GCC 4.6.1), this is printed:
A(13)
B()
~B()
~A()
0
Obviously, 0 is wrong, because A is already destroyed. It looks like this is actually captured and is used to look up this->a_. My suspicion is confirmed when I change the capture list from [=] to [=,a_]. Then the correct output is printed and the lifetime of the objects is as expected:
A(13)
B()
~B()
13
~A()
The question:
Is this behaviour specified by the standard, implementation-defined, or undefined? Or I'm crazy and it's something entirely different?
Is this behaviour specified by the standard
Yes. Capturing member variables is always done via capturing this; it is the only way to access a member variable. In the scope of a member function a_ is equivalent to (*this).a_. This is true in Lambdas as well.
Therefore, if you use this (implicitly or explicitly), then you must ensure that the object remains alive while the lambda instance is around.
If you want to capture it by value, you must explicitly do so:
std::function<void()> getf()
{
auto varA = a_;
return [=]() { varA->a(); };
}
If you need a spec quote:
The lambda-expression’s compound-statement yields the function-body ( 8.4 ) of the function call operator, but for purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming id-expressions referring to non-static class members into class member access expressions using (*this) ( 9.3.1 ),
the compound-statement is considered in the context of the lambda-expression.