How is a class made "MoveInsertable"?

47 Views Asked by At

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
0

There are 0 best solutions below