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

P2786中is_replaceable的用途与语义是什么?

关于C++可替换类型is_replaceable<T>的实践困惑与应用疑问

C++工作草案曾新增[编辑说明:截至2025年11月已再次移除!但为留存历史价值,本讨论将开放至2026年左右]了可替换类型(is_replaceable<T>)的定义,该定义来自提案P2786R13。P2786R13中对其的说明为:

可替换性是类型的一种语义属性,即移动赋值等价于销毁后再移动构造。

libc++正考虑添加该特性并将其作为优化判断条件,但在实践中对其实际含义产生了困惑。例如shared_ptr<int>是否属于“可替换类型”?显然,我们不能将如下移动赋值代码:

void f(shared_ptr<int>& a, shared_ptr<int>& b) {
  a = std::move(b);
}

直接替换为销毁后构造的版本:

void f(shared_ptr<int>& a, shared_ptr<int>& b) {
  a.~shared_ptr();
  ::new (&a) shared_ptr<int>(std::move(b));
}

因为如果b的生命周期由a控制,会引发两个问题:(1) a的引用计数会不合时宜地降至0;(2) b会在其生命周期外被访问。此前所有库类型的自移动赋值均为未定义行为*[编辑说明:现已不再是]*,但一般赋值场景(如下示例)的行为应当是明确的:

struct S {
  int i_;
  std::shared_ptr<int> p_;
};

int main() {
  std::shared_ptr<S> oa = std::make_shared<S>();
  std::shared_ptr<int> a(oa, &oa->i_);
  std::shared_ptr<int>& b = oa->p_;
  b = std::make_shared<int>(42);
  oa = nullptr;
  assert(a.use_count() == 1);
  assert(b.use_count() == 1);
  f(a, b); // 移动赋值(行为明确),或销毁后构造(引入未定义行为)
  printf("a.use_count() is %zu (should be 1)\n", a.use_count());
  printf("*a is %d (should be 42)\n", *a);
}

is_replaceable的实际含义与其预期支持的优化类型密切相关。或许其设计意图并非让开发者直接将赋值替换为销毁后构造,而是仅在is_replaceable && is_trivially_relocatable(即库厂商需求的P1144特性)同时满足时才启用优化。若如此,libc++将shared_ptr标记为is_replaceable是“安全”的,只是用户需理解该库特性与标准文档中的描述并非完全一致。

最初设想is_replaceable可用于vector::erase这类场景,但实际情况是:假设V为pmr::vector<int>,我们希望优化pmr::vector<V>::erase,但is_replaceable_v<V>的值为false。因此is_replaceable无法单独区分哪些类型适合insert/erase优化,仍需在vector中编写特殊逻辑来专门处理PMR类型。

那么库厂商该如何运用这个新特性?

内容的提问来源于stack exchange,提问作者Quuxplusone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 18:34:55