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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With