std :: unique_ptr是否在其析构函数中将其基础指针设置为nullptr?

时间:2019-01-17 13:36:08

标签: c++ unique-ptr nullptr

在实现自己的unique_ptr(只是为了好玩)时,我发现它无法通过libstdcxx传递此test file

struct A;

struct B
{
  std::unique_ptr<A> a;
};

struct A
{
  B* b;
  ~A() { VERIFY(b->a != nullptr); }
};

void test01()
{
  B b;
  b.a.reset(new A);
  b.a->b = &b;
}

gcc passes会很高兴地看到这个测试文件(当然,这个文件来自libstdcxx),而VERIFY的部分是clang fails

问题:

  1. 是实现相关的还是未定义的行为?
  2. 我猜想这个后置条件(b->a != nullptr)对于gcc很重要,否则它将没有测试文件,但我不知道它的背后是什么。与优化有关吗?我知道很多UB都是为了更好的优化。

3 个答案:

答案 0 :(得分:8)

clang(libc ++)在这一点上似乎不合规,因为该标准指出:

  

[unique.ptr.single.dtor]

~unique_ptr();
     
      
  1. 要求:表达式get_­deleter()(get())格式正确,行为明确,并且不会引发异常。   [注意:使用default_­delete要求T是完整类型。   — 尾注   ]

  2.   
  3. 效果:如果get() == nullptr没有效果。   否则为get_­deleter()(get())

  4.   

因此,析构函数应等效于get_deleter()(get()),这意味着b->a不能在nullptr的析构函数中被A(在{{1}内部调用) },方法是get_deleter()


另一方面,delete(libc ++)和clang(libstdc ++)在销毁gcc时都将指针设置为nullptr,但这是{{1 }}析构函数:

std::unique_ptr

...这是gcc(呼叫auto& __ptr = _M_t._M_ptr(); if (__ptr != nullptr) get_deleter()(__ptr); __ptr = pointer(); ):

clang

如您所见,reset()首先删除然后分配给pointer __tmp = __ptr_.first(); __ptr_.first() = pointer(); if (__tmp) __ptr_.second()(__tmp); gcc),而nullptr首先分配给pointer()clang ),然后删除 1


1 nullptr是与pointer()对应的别名(如果存在),或者简称为pointer

答案 1 :(得分:3)

libstdc++ 和 libc++ 都是一致的,因为这是一个定义良好的程序无法观察到的。在析构函数的执行过程中,[res.on.objects]/2 禁止在未定义行为的痛苦中观察(或修改,就此而言)unique_ptr 的状态:

<块引用>

如果访问标准库类型的对象,并且对象生命周期的开始没有在访问之前发生,或者访问没有在对象生命周期结束之前发生,除非另有说明,否则行为未定义。

事实上,unique_ptr 的析构函数为什么首先添加了这个段落(由 LWG2224)。

另外,销毁完成后,它占用的存储内容由[basic.life]/4不确定:

<块引用>

本文档中赋予对象和引用的属性仅适用于给定对象或引用在其生命周期内。

答案 2 :(得分:0)

销毁后std::unique_ptr<>占用的内存的最终状态没有要求。将其设置为null毫无意义,因为内存将返回到分配它的位置。 GCC可能会检查它是否为非null,以确保没有人添加不必要的代码来清除它。在适当的情况下,在不需要时强制清除该值可能会导致性能下降。