Resize on std::vector does not call move construct

2019-04-06 11:35发布

问题:

This question already has an answer here:

  • C++11 rvalue reference calling copy constructor too 4 answers

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?

回答1:

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.



回答2:

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.)