Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making sure move constructor gets called

I have the following simplified code example:

#include <algorithm>
#include <iostream>

using namespace std;

class ShouldBeMovedWhenSwapped
{
public:
//  ShouldBeMovedWhenSwapped() = default;
//  ShouldBeMovedWhenSwapped(ShouldBeMovedWhenSwapped&&) = default;
//  ShouldBeMovedWhenSwapped(const ShouldBeMovedWhenSwapped&) = default;
//  ShouldBeMovedWhenSwapped& operator=(ShouldBeMovedWhenSwapped&&) = default;

    struct MoveTester
    {
        MoveTester() {}
        MoveTester(const MoveTester&) { cout << "tester copied " << endl; }
        MoveTester(MoveTester&&) { cout << "tester moved " << endl; }
        MoveTester& operator=(MoveTester) { cout << "tester emplaced" << endl; return *this; } // must be declared if move declared
    };

    MoveTester tester;
};

int main()
{
    ShouldBeMovedWhenSwapped a;
    ShouldBeMovedWhenSwapped b;
    std::swap(a,b);
    return 0;
}

I'm using MinGW, while running 'gcc --version' i get gcc 4.7.2

EDIT: for the first question see the comments in the question. It appears to be a bug in gcc.

The output of the code depends on which constructors are commented out. But i don't understand why the differences occur. What is the reason behind each output?

// Everything commented out
tester moved 
tester copied <---- why not moved?
tester emplaced
tester copied <---- why not moved?
tester emplaced

// Nothing commented out
tester moved
tester moved
tester emplaced
tester moved
tester emplaced

// Move constructor commented out
tester copied
tester moved
tester emplaced
tester moved
tester emplaced

For my second question (which was why i started this test) - Let's say i have a real case with a large vector instead of the class MoveTester, how can i be sure the vector is moved instead of being copied in such cases?

like image 802
user1708860 Avatar asked Dec 21 '15 20:12

user1708860


1 Answers

The first part of the problem is an outdated compiler, but there's another one: you declared MoveTester::operator= in suboptimal way - it takes its argument by value, so a copy/move constructor is invoked one extra time. Try this version of MoveTester:

struct MoveTester
{
    MoveTester() {}
    MoveTester(const MoveTester&) { cout << "tester copied " << endl; }
    MoveTester(MoveTester&&) { cout << "tester moved " << endl; }
    MoveTester& operator=(const MoveTester&) { cout << "tester copy assignment" << endl; return *this; } // must be declared if move declared
    MoveTester& operator=(MoveTester&&) { cout << "tester move assignment" << endl; return *this; } // must be declared if move declared
};

I'm getting the following output:

tester moved 
tester move assignment
tester move assignment

Perhaps you'll get something similar even with GCC 4.7.


Regarding your second question, the move constructor of std::vector is guaranteed by standard to have constant time complexity. The question is whether the compiler obeys the standard. I believe the only way to make sure is to debug or to profile your code.

like image 153
Anton Savin Avatar answered Oct 06 '22 19:10

Anton Savin