`std::weak_ptr` 何时为空?

3
根据 [util.smartptr.weak.obs]/4weak_ptr::owner_before 严格弱序是这样的:"如果两个 shared_­ptr 或者 weak_­ptr 具有相同的所有权或者都为空,则它们等价。"
然而,在标准中我并没有看到定义 weak_ptr 空的内容。显然,一个默认构造的 weak_ptr 是空的,由一个空 shared_ptr 构造的 weak_ptr 也是空的,但是看起来并没有明确说明过一个已经过期的 weak_ptr 是否为空。

1
我认为一个空的weak_ptr是指use_count()返回0的情况,因为这似乎是默认构造、移动和过期的weak_ptr能够同质地使用lock()expired()所必需的。 - François Andrieux
1
对于 shared_ptr"empty" is defined 的定义为:“当其未拥有一个指针时,称 shared_­ptr 为空。”-- 显然,对于 weak_ptr,此定义并不适用,因为 weak_ptr 永远不会拥有一个指针。 - Ben Voigt
@FrançoisAndrieux:但这个论点是无效的,因为它未考虑“非空,拥有一个与零个生存的shared_ptr实例共享的元数据块”作为一种可能性。这种情况在任何规范中都没有被排除。直接回应逻辑论点——“空”意味着use_count() == 0,而expired() == true等同于use_count() == 0,但expired() == 0并不意味着“空”,因为use_count() == 0并不意味着“空”。声称如此是犯了逆命题谬误 - Ben Voigt
抽象地说,一个weak_ptr为空并没有什么“意义”;它只是一个weak_ptr对象可能具有的特定状态的名称。定义如何将对象置于该状态或从该状态中取出以及空状态对各种操作的影响就足够了。但是,过期的weak_ptr是否为空仍然是一个有效的问题,并且可以通过询问过期的weak_ptr是否必须等于空的weak_ptr来使其更加具体化。 - Nate Eldredge
这确实引发了一些其他问题,关于owner_before()在指向已过期的指针上应该如何行为。在gcc/clang测试中,似乎两个曾经共享同一对象所有权的weak_ptr在后来过期后,在owner_before()下仍然是等价的。这意味着尽管“拥有”的对象不再存在,它们仍然“共享所有权”。 - Nate Eldredge
显示剩余7条评论
1个回答

2

虽然标准没有保证,但是owner_before排序的可用性1要求:

  • 一个已经过期2weak_ptr不为空。

这是因为只有对象的更改才应该改变其在排序中的位置,而weak_ptr可能会在没有被更改的情况下过期。


1例如,这里有人将weak_ptr用作std::set中的键:How to compute hash of std::weak_ptr?。这样做,以及std::map同样需要排序来保证过期后仍可使用。如果有一个owner_hash以允许与std::unordered_setstd::unordered_map一起使用,那么该哈希值也必须在过期后仍可使用。


2注意“已过期”和“过期”的微妙区别——构造为空的weak_ptr,或者赋值为空指针值的weak_ptr,都具有expired() == true。但它并没有过期(动词),它是在过期状态下创建的。我的答案仅适用于weak_ptr值在某个时刻未过期,然后由于从目标中分离shared_ptr值而(被动地)变为过期状态。


gcc和clang都表现出这样的行为:一个已过期的指针在owner_before下不等同于空指针。https://godbolt.org/z/4e76jzvT3 - Nate Eldredge
我认为标准其实是隐含保证了这一点。我们通常假设操作仅具有标准规定的可观察效果,没有更多的效果。共享所有权的 weak_ptr 显然不为空,并且标准中没有任何内容说明它在过期时变为空。因此,我们应该得出结论:它的空状态在过期时不会改变,即它仍然是非空的。 - Nate Eldredge
事实上,标准指定 weak_ptr 变为空的唯一方式是默认构造、从空指针进行复制构造、移动构造、与空指针交换,或者执行其他被定义为执行这些特定操作之一的操作(例如将其赋值为空指针或 reset())。因此,我们可以假设 weak_ptr 不会通过任何其他方式变为空(当然,除了未定义行为)。 - Nate Eldredge

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