最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

optimization - What is the purposesemantics of P2786 is_replaceable? - Stack Overflow

programmeradmin5浏览0评论

The C++ working draft now has a definition of replaceable type (is_replaceable<T>), which it got from the just-adopted P2786R13. P2786R13 itself says:

Replaceability is a semantic property of a type, where move assignment is isomorphic to destroy then move-construct.

libc++ is thinking about adding that trait and trying to gate optimizations on it, but they're running into confusion about what this should actually mean in practice. Is shared_ptr<int> "replaceable"? After all, it seems to be not true that you could replace

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

with

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

because if b's lifetime is controlled by a, then (1) the refcount of a would drop to zero when it shouldn't, and (2) b would be accessed outside its lifetime. Now, self-move-assignment is UB for all library types [EDIT: not anymore], but assignment in a general situation like this is not supposed to be UB, AFAIK.

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); // either move-assign (well-defined), or destroy-and-construct (introduces UB)
  printf("a.use_count() is %zu (should be 1)\n", a.use_count());
  printf("*a is %d (should be 42)\n", *a);
}

The question of what is_replaceable is supposed to mean in practice, is closely related to the question of what optimizations are intended to be gated on is_replaceable.

Perhaps the intent is that nobody should ever actually write an optimization that replaces assignment with destroy-and-construct; they should only ever gate on the conjunction of is_replaceable && is_trivially_relocatable (i.e. the P1144 trait that library vendors were asking for). In that case, it would be "safe" for libc++ to mark its shared_ptr as is_replaceable; it would just fall on the user's shoulders to understand that this library trait doesn't mean quite what it says in the paper standard.

IIUC, the original hypothesis was that is_replaceable would be useful for things like vector::erase. But in fact — let V be pmr::vector<int>; then we want to optimize pmr::vector<V>::erase, but is_replaceable_v<V> is false. So is_replaceable doesn't suffice to distinguish which types should get the insert/erase optimization, either; we still need to write a special case into vector to handle PMR types specifically.

What can library vendors do with this new trait?

发布评论

评论列表(0)

  1. 暂无评论