11

Why does the following code not work with g++ version 4.9.2? If I try to build it the compiler complains about the missing copy constructor in the line where the vector is told to reserve more memory.

#include <vector>

class Number
{
public:
    explicit Number(const int& i) : _value(i) {}

    Number(const Number& other) = delete;
    Number& operator=(const Number& other) = delete;

private:
    int _value;
};


int main(int argc, char** argv)
{
    std::vector<Number> numbers;
    numbers.reserve(8);

    return 0;
}

Why would the compiler even try to call the deleted copy constructor when the storage size of the vector is increased? There are no objects inside the vector anyway.

ifschleife
  • 1,055
  • 1
  • 10
  • 21
  • Reserve will need to copy existing elements over if the capacity is increased. – juanchopanza Dec 14 '14 at 15:35
  • I does not matter, whether the vector contains elements or not, a proper reserve has to consider possible existing elements. Hence the instantiation of the copy constructor (if memory gets reallocated) –  Dec 14 '14 at 15:37

1 Answers1

14

The short answer is because the language standard says so. But that isn't interesting.

Reserve can call the copy constructor if there was data in the container.

Which branch of reserve it follows (no copy, or copy) is determined at run time, not compile time.

You are seeing a compile time error, not a run time error. You have stated that no code that can possibly cause your object to copy should be compiled.

The compiler does not analyze the spot where reserve is, prove to itself the vector is empty, use that to determine which branch of code reserve will run, then say "no problem". Instead, it compiles the function, which contains copies, and generates an error.

In theory, a container that allows pre-allocation (while empty) and emplace construction only (up to the pre-determined limit) would not require objects with any kind of copy or move construction. At the time the std container library was written, emplace construction was impractical, so this option does not exist in the std container library: prior to C++11, the only way to put objects into a vector was to copy it.

std::dynarray comes close, but it doesn't let you have the buffer be half-unused and filled gradually.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Very interesting, thank you. I'm not sure I fully understand it though. I wanted to test the new move semantics of `c++11` and later. Further code would have called `emplace_back` and such. Does your answer imply that even when only move semantics are used the object to be stored in the `vector` needs a copy constructor? What about `unique_ptr`? Even though it can't be copied it still needs a copy constructor just so it can be placed inside a `vector`? – ifschleife Dec 15 '14 at 10:37
  • 1
    @ifsch move would work fine, copy only needed by, well, vector copy. – Yakk - Adam Nevraumont Dec 15 '14 at 12:00
  • Yakk is correct. As a side note, [`boost::container::vector`](https://www.boost.org/doc/libs/1_71_0/doc/html/container/main_features.html) is similar to the standard vector but doesn't require copy constructability for reserving capacity. – metal Aug 26 '19 at 14:27