为什么std::vector::reserve会调用复制构造函数?

11
为什么下面的代码在 g++ 版本 4.9.2 上无法工作?如果我尝试构建它,编译器会抱怨在告诉向量保留更多内存的行中缺少复制构造函数。

为什么以下代码在 g++ 版本 4.9.2 中无法使用?如果我尝试进行构建,则编译器会抱怨在告知向量保留更多内存的那一行中缺少复制构造函数。

#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;
}

当向量的存储大小增加时,为什么编译器会尝试调用已删除的复制构造函数?无论如何,向量内部都没有对象。


如果要增加容量,Reserve 将需要复制现有元素。 - juanchopanza
不管向量是否包含元素,正确的保留应考虑可能存在的元素。因此,如果需要重新分配内存,则必须实例化复制构造函数。 - user2249683
1个回答

14

简单来说,这是因为语言标准规定了这样的行为。但这并不有趣。

如果容器中已经有数据,reserve方法将调用复制构造函数。

reserve方法采取哪个分支(不复制或复制)是在运行时而非编译时确定的。

你看到的是编译时错误而非运行时错误。你已经声明编译时不会产生可以导致对象复制的任何代码。

编译器不会分析reserve所在的位置,证明vector为空,并根据此确定reserve将运行哪个代码分支,然后说“没问题”。相反,它编译包含副本的函数并生成错误。

理论上,一个允许预先分配(空的)并仅使用emplace构造(最多到预定限制)的容器不需要具备任何类型的复制或移动构造。在std容器库写入时,emplace构造是不切实际的,因此在std容器库中不存在这个选项:在C++11之前,将对象放入vector的唯一方法是复制它。

std::dynarray接近这个目标,但它不允许缓冲区半未使用并逐渐填充。


非常有趣,谢谢。不过我不确定我完全理解了。我想测试 c++11 及更高版本的新移动语义。进一步的代码将调用 emplace_back 等函数。您的回答是否意味着即使只使用移动语义,要存储在 vector 中的对象仍需要一个复制构造函数?那 unique_ptr 呢?即使它不能被复制,它仍然需要一个复制构造函数才能放置在 vector 中吗? - ifschleife
1
@ifsch 移动操作完全可以正常工作,只有在需要向量复制时才需要复制。 - Yakk - Adam Nevraumont
Yakk 是正确的。另外,boost::container::vector 类似于标准向量,但不需要复制构造函数来保留容量。 - metal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接