Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resize on std::vector does not call move constructor [duplicate]

I have been playing around with std::vector to understand when objects are constructed, destructed, copy constructed and move constructed. To so do, I have written the following program

#include <iostream>
#include <vector>

class Test {
public:
    Test() {
        std::cout << "Constructor called for " << this << std::endl;
    }
    Test(const Test& x) {
        std::cout << "Copy Constructor called for " << this << std::endl;
    }
    Test(Test&& x) {
        std::cout << "Move Constructor called for " << this << std::endl;
    }
    ~Test() {
        std::cout << "Destructor called for " << this << std::endl;
    }
};

int main() {
    std::vector<Test> a( 1 );
    a.resize(3);

    return 0;
}

When a is resized, reallocation happens. My guess would have been that the object a[0] is moved constructed to the new a[0]. But, with libc++ and libstdc++, it seems that the copy constructor is called and not the move constructor. Is there any reason for such a behaviour?

like image 832
InsideLoop Avatar asked Jan 27 '15 08:01

InsideLoop


People also ask

What happens when a vector is resized?

vector::resize() The function alters the container's content in actual by inserting or deleting the elements from it. It happens so, If the given value of n is less than the size at present then extra elements are demolished.

Does std :: move do a copy?

std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.

Does Emplace_back call copy constructor?

This code demonstrates that emplace_back calls the copy constructor of A for some reason to copy the first element. But if you leave copy constructor as deleted, it will use move constructor instead.

How do you resize a std::vector?

The C++ function std::vector::resize() changes the size of vector. If n is smaller than current size then extra elements are destroyed. If n is greater than current container size then new elements are inserted at the end of vector. If val is specified then new elements are initialed with val.


2 Answers

I just found the answer to the question. The move constructor has to be declared noexcept to do so. When such a change has been done

Test(Test&& x) noexcept {
    std::cout << "Move Constructor called for " << this << std::endl;
}

the move constructor is called.

like image 86
InsideLoop Avatar answered Sep 18 '22 12:09

InsideLoop


Just as @InsideLoop's answer said, the move constructor has to be declared "noexcept" to be called.

It is because in the vector::resize() function call stack, we can find move_if_noexcept() is called in function __construct_backward().(see [your library path]/include/c++/v1/memory line:1531)

construct(__a, _VSTD::__to_raw_pointer(__end2-1), _VSTD::move_if_noexcept(*--__end1));

According to the C++11 standard, we know that move constructor is used for temporary right value in normal. So the move constructor throwing exception is such a dangerous thing, and we can declare "noexcept" for the move constructor to avoid it.

Using move_if_noexcept(), although it loss in performance, but can make the process safe. And the function will activate the move constructor when move constructor is declared "noexcept".

(Sorry for my poor English.)

like image 42
YuChan Avatar answered Sep 21 '22 12:09

YuChan