During optimization I've discovered many unexpected copies when using std::vector<T>. I have defined ctor, copy, move, copy-assign and move-assign for my <T>. I can force a move instead of copy by using std::vector<T>.push_back(std::move(T)). However, as the vector grows and auto-resizes, the resulting operation copies the <T> objects into the grown vector rather than moving them. cppreference indicates requirement that is "MoveAssignable" for moves. If that is the issue, how do I make MoveAssignable? I can't even find the associated std::is_move_insertable_v<T>.
simplified code here:
#include <iostream>
#include <string>
#include <vector>
#include <experimental/type_traits>
int main(int argc, char** argv) {
class Mystr {
public:
Mystr() { std::cout << "ctor\n"; }
Mystr(const std::string &str) : ms(str) { std::cout << "ctor " << ms << '\n'; }
Mystr(const Mystr& other) : ms(other.ms) { std::cout << "copy n:o " << ms << ':' << other.ms << '\n'; }
Mystr(Mystr&& other) : ms(std::move(other.ms)) { std::cout << "move n:o " << ms << ':' << other.ms << '\n'; }
Mystr& operator=(const Mystr& other) { ms=other.ms; std::cout << "copy-assign n:o" << ms << ':' << other.ms << '\n'; return *this; }
Mystr& operator=(Mystr&& other) { ms=std::move(other.ms); std::cout << "move-assign n:o " << ms << ':' << other.ms << '\n'; return *this; }
std::string ms;
};
std::vector<Mystr> mvec;
std::cout << "--compiler default--\n";
for (int c=0; c<5; c++) {
Mystr mstr(std::to_string(c));
std::cout << "--push s/c " << mvec.size() << '/' << mvec.capacity() << '\n';
mvec.push_back(mstr);
std::cout << "++push s/c " << mvec.size() << '/' << mvec.capacity() << '\n';
}
mvec.clear();
mvec.shrink_to_fit();
std::cout << "--forced move--\n";
for (int c=0; c<5; c++) {
Mystr mstr(std::to_string(c));
std::cout << "--push s/c " << mvec.size() << '/' << mvec.capacity() << '\n';
mvec.push_back(std::move(mstr));
std::cout << "++push s/c " << mvec.size() << '/' << mvec.capacity() << '\n';
}
std::cout << "'Mystr' is " << (std::is_move_constructible_v<Mystr>?"":"not ") << "move-constuctible\n";
std::cout << "'Mystr' is " << (std::is_move_assignable_v<Mystr>?"":"not ") << "move-assignable\n";
// std::cout << "'Mystr' is " << (std::is_move_insertable_v<Mystr>?"":"not ") << "move-insertable\n";
std::cout << "'Mystr' is " << (std::is_copy_constructible_v<Mystr>?"":"not ") << "copy-constuctible\n";
std::cout << "'Mystr' is " << (std::is_copy_assignable_v<Mystr>?"":"not ") << "copy-assignable\n";
// std::cout << "'Mystr' is " << (std::is_copy_insertable_v<Mystr>?"":"not ") << "copy-insertable\n";
} // main()
output here:
--compiler default--
ctor 0
--push s/c 0/0
copy n:o 0:0
++push s/c 1/1
ctor 1
--push s/c 1/1
copy n:o 1:1
copy n:o 0:0
++push s/c 2/2
ctor 2
--push s/c 2/2
copy n:o 2:2
copy n:o 0:0
copy n:o 1:1
++push s/c 3/4
ctor 3
--push s/c 3/4
copy n:o 3:3
++push s/c 4/4
ctor 4
--push s/c 4/4
copy n:o 4:4
copy n:o 0:0
copy n:o 1:1
copy n:o 2:2
copy n:o 3:3
++push s/c 5/8
--forced move--
ctor 0
--push s/c 0/0
move n:o 0:
++push s/c 1/1
ctor 1
--push s/c 1/1
move n:o 1:
copy n:o 0:0
++push s/c 2/2
ctor 2
--push s/c 2/2
move n:o 2:
copy n:o 0:0
copy n:o 1:1
++push s/c 3/4
ctor 3
--push s/c 3/4
move n:o 3:
++push s/c 4/4
ctor 4
--push s/c 4/4
move n:o 4:
copy n:o 0:0
copy n:o 1:1
copy n:o 2:2
copy n:o 3:3
++push s/c 5/8
'Mystr' is move-constuctible
'Mystr' is move-assignable
'Mystr' is copy-constuctible
'Mystr' is copy-assignable