Michael IV
Michael IV

Reputation: 11424

Is default move constructor optimized away by compiler?

I have an std::multimap of the following type:

 typedef std::multimap<std::pair<bool,uint32_t>, FooObject>

The FooObject has default move copy and assign constructors declared:

   FooObject(FooObject&& v) = default;
   FooObject& operator=(FooObject&& other)=default;

The copy/assign constructors are private to disable implicit copy.

So I should be able to emplace a pair into the map like this:

mymap.emplace(std::make_pair(false,32),FooObject());

This throws a list of errors with the one at the end:

error C2660: 'std::pair::pair': function does not take 2 arguments

If I declare move copy assign constructors without "default" then it compiles ok.

   FooObject(FooObject&& v){}
   FooObject& operator=(FooObject&& other){}

Why is that? Does the compiler optimize away these constructors when marked with "default" keyword? I am using MSVC140

UPDATE:

Based on the comments below I found the reason - FooObject has a non-copiable member instance. Here is the FooObject:

#define NO_COPY_ASSIGN(TypeName)   \                                                     
  TypeName (const TypeName &);     \                                                                
  void operator= (const TypeName &);

class FooObject
{
private:
   NO_COPY_ASSIGN(FooObject)
public:
    struct FooStruct
    {
        FooBuffer   frameBuffer;   //<--Here it is
    }fooStruct;

    FooObject(){}


    /** Move constructor to allow insert into vector without copy */
    FooObject(FooObject&& v) = default;
    FooObject& operator=(FooObject&& other) = default;

};

*FooBuffer has also its copy/assign private.But I still don't get why replacing 'default' with {} fixes that.Please explain.

Upvotes: 3

Views: 245

Answers (2)

NathanOliver
NathanOliver

Reputation: 180435

The difference between

FooObject(FooObject&& v) = default;

and

FooObject(FooObject&& v){}

Is that the former will emit a constructor that moves each member from v while the latter default constructs each member and does nothing with v.

Since FooBuffer is not movable that means that the compiler will delete FooObject(FooObject&& v) = default; as it would be ill-formed.

With FooObject(FooObject&& v){} you do not have that problem as you never try to move the members of v. Since there is no member initialization list the compiler will add one for you that just default constructs the members.

You can see this behavior more explicitly with this:

struct Moveable
{
    Moveable() = default;
    Moveable(Moveable&&) { std::cout << "in Moveable(Moveable&&)\n"; }
};

struct Foo
{
    Foo() = default;
    Foo(Foo&&) = default;
    Moveable m;
};

struct Bar
{
    Bar() = default;
    Bar(Bar&&){}
    Moveable m;
};

int main()
{
    Foo f;
    Bar b;
    std::cout << "test_f\n";
    Foo test_f(std::move(f));
    std::cout << "test_b\n";
    Bar test_b(std::move(b));
}

which outputs

test_f
in Moveable(Moveable&&)
test_b

Live Example

Showing that nothing is actually moved in Bar's move constructor.

Upvotes: 1

Mark B
Mark B

Reputation: 96233

Your problem is that one of the member of FooObject is not move-able, which prevents the compiler from generating default move operations.

The {} versions of the move operations you implement yourself do no work (specifically: they don't actually do a move operation) on the members of FooObject and are thus legal.

Upvotes: 2

Related Questions