Why does member initilizer invoke extra constructor call when moved?

时间:2019-04-16 22:08:21

标签: c++ constructor language-lawyer move-semantics move-constructor

When a member initializer is used in a class that has a move constructor the initilized member's constructor gets called when the enclosing class is moved. Why does this happen? Please provide references to the standard. I have a guess as to what's happening that I give with the example results below.

Also, in slightly different scenrio, why doesn't the member's constructor get called if the initialized member is a plain-old-data type?

Also, what are best practices concerning member initializers and move constructors?

#include <bits/stdc++.h>

using namespace std;
struct C {
    void do_stuff(){cout<<"stuff";}
    C(){cout<<"C ctor"<<endl;}
    ~C(){cout<<"C DTOR"<<endl;}
};
struct Foo {

ifdef MEMBER_INIT
    Foo() {cout<<"Foo ctor"<<endl;};
#else
    Foo() : ptr(new C) {cout<<"Foo ctor"<<endl;};
#endif

    Foo(Foo &) = delete;
    Foo & operator=(Foo &) = delete;
    Foo & operator=(Foo &&) = delete;
    Foo(Foo && rhs){cout<<"Foo MOVE ctor"<<endl; rhs.ptr.swap(this->ptr); }
    ~Foo(){cout << "Foo DTOR "; if(ptr) ptr->do_stuff(); cout<<endl; }

#ifdef MEMBER_INIT
    unique_ptr<C> ptr = make_unique<C>();
#else
    unique_ptr<C> ptr;
#endif

};

int main()
{
    Foo f;
    Foo f2(move(f));
}

RESULTS:

g++ -std=c++14 x.cc && ./a.out
    C ctor
    Foo ctor
    Foo MOVE ctor
    Foo DTOR stuff
    C DTOR
    Foo DTOR

g++ -DMEMBER_INIT -std=c++14 x.cc && ./a.out
    C ctor
    Foo ctor
    C ctor
    Foo MOVE ctor
    Foo DTOR stuff
    C DTOR
    Foo DTOR stuff
    C DTOR

Why does using the member initializer invoke another constructor call for C? Why does using the member initializer cause the Foo destructor to run C->do_stuff()?

My quess is that member initializers get evaluated for ALL constructor types before the actual constructor (in this case a move constructor) gets run. Is that correct?

I would specifically like references in the standard that verify or contradict my guess.

1 个答案:

答案 0 :(得分:0)

When MEMBER_INIT is defined move constructor performs ptr initialization using in-class initializer and becomes

Foo(Foo && rhs): ptr{make_unique<C>()}

Otherwise it is default-initialized.

15.6.2 Initializing bases and members [class.base.init]
9 In a non-delegating constructor, if a given potentially constructed subobject is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then

the entity is initialized from its default member initializer as specified in 11.6;

Basically you forgot to actually manually initialize ptr field by moving:

Foo(Foo && rhs): ptr{::std::move(rhs.ptr)}