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

陳 力*_*陳 力 4 c++ unique-ptr nullptr

当实现我自己的unique_ptr(只是为了好玩),我发现它无法通过这个测试文件来自libstdcxx:

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;
}
Run Code Online (Sandbox Code Playgroud)

gcc愉快地传递了这个测试文件(当然,这个文件是来自libstdcxx),而clang因为VERIFY部分失败了.

题:

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

Hol*_*olt 8

clang (libc ++)似乎在这一点上是不合规的,因为标准说:

[unique.ptr.single.dtor]

~unique_ptr();
Run Code Online (Sandbox Code Playgroud)
  1. 要求:表达式get_­deleter()(get())应格式良好,具有明确定义的行为,不得抛出异常.[  注意:使用default_­delete要求T是完整的类型.-  结束说明  ]

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

所以析构函数应该等价get_deleter()(get()),这意味着它b->a不能nullptr在析构函数中A(get_deleter()delete指令调用内部).


在旁注中,clang(libc ++)和gcc(libstdc ++)都将指针设置为nullptr在销毁a时std::unique_ptr,但这里是gcc析构函数:

auto& __ptr = _M_t._M_ptr();
if (__ptr != nullptr)
    get_deleter()(__ptr);
__ptr = pointer();
Run Code Online (Sandbox Code Playgroud)

......这里是clang(致电reset()):

pointer __tmp = __ptr_.first();
__ptr_.first() = pointer();
if (__tmp)
   __ptr_.second()(__tmp);
Run Code Online (Sandbox Code Playgroud)

如您所见,gcc首先删除然后分配给nullptr(pointer()),同时clang首先分配给nullptr(pointer())然后删除1.


1 pointer是对应于Deleter::pointer(如果存在)或简单的别名T*.