You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何不可复制不可移动类型的pair用emplace_back存入std::vector会报错

问题描述

首先定义如下不可复制、不可移动,无默认构造函数也无单参数构造函数的结构体X:

struct X
{
    X(int x, int y) { }
    X(const X&) = delete;
    X(X&&) = delete;
};

现有容器std::vector<std::pair<X,X>> v,若X支持单参数构造,我们可以直接使用emplace_back调用std::pair<X,X>的构造函数完成插入。

如果写成如下形式:

v.emplace_back(X(42,42),X(69,69));

会触发X的移动构造函数调用,由于移动构造被显式删除,代码无法编译。因此我们改用std::pair的std::piecewise_construct分段构造语法,写法如下:

v.emplace_back(std::piecewise_construct, std::forward_as_tuple(42,42), std::forward_as_tuple(69,69));

原本预期该写法可以正常编译运行,但实际发现std::vector仍然会尝试调用X的移动构造或拷贝构造函数,导致编译失败。

但如果将容器替换为std::list,上述代码就能正常运行;给X添加<运算符后,创建std::map<X,X>(其节点为X组成的pair)或是std::set<std::pair<X,X>>,使用emplace而非emplace_back插入也能正常工作。


原因分析

这个差异本质上是不同STL容器的内存模型决定的:

  • std::list、std::map、std::set都属于节点式容器:每个元素被存储在独立分配的堆节点中,插入新元素时仅需要创建新节点、调整容器内部的指针指向,永远不需要移动已存在的元素。因此这类容器只要求元素支持原地构造即可,不需要具备拷贝或移动能力。
  • std::vector属于连续存储容器:所有元素存储在一整块连续的内存空间中,当插入元素导致当前容量不足时,容器必须申请一块更大的连续内存,将已有元素全部转移到新内存后再释放旧内存。这个转移过程必须调用元素的拷贝构造或者移动构造函数。

由于X类型显式删除了拷贝和移动构造,导致std::pair<X,X>的拷贝、移动构造也被隐式删除,无法满足vector对元素类型的要求,因此编译失败。即便你当前插入时vector剩余容量足够,编译器仍然会做语法检查——因为vector后续随时可能因为新的插入触发扩容,必须保证转移元素的能力存在,所以即使本次插入不需要扩容也无法通过编译。


内容的提问来源于stack exchange,提问作者Michal Dvořák

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 05:24:04