Устанавливает ли std :: unique_ptr свой указатель на nullptr внутри своего деструктора?

Когда вы передаете Func<TResult> в метод Run<TResult>(Func<TResult>), вам не нужно указывать общий код метода, потому что он может его вывести. Ваша лямбда делает этот вывод.

Однако ваша функция на самом деле не является Func<TResult>, тогда как лямбда была.

Если вы выполняете Func<Int32> f = MyIntReturningMethod, это работает. Теперь, если вы укажете Task.Run<Int32>(MyIntReturningMethod), вы ожидаете, что он тоже будет работать. Однако он не может решить, должно ли оно разрешить перегрузку Func<Task<TResult>> или перегрузку Func<TResult>, и это не имеет особого смысла, поскольку очевидно, что метод не возвращает задачу.

Если вы скомпилируете что-то вроде следующего:

void Main()
{
    Thing(MyIntReturningMethod);
}


public void Thing<T>(Func<T> o)
{
    o();
}

public Int32 MyIntReturningMethod()
{
return (5);
}

IL выглядит так:

IL_0001:  ldarg.0     
IL_0002:  ldarg.0     
IL_0003:  ldftn       UserQuery.MyIntReturningMethod
IL_0009:  newobj      System.Func<System.Int32>..ctor
IL_000E:  call        UserQuery.Thing

(Некоторые из дополнительных материалов из добавлений LINQ Pad ... как часть UserQuery)

IL выглядит одинаково, как если бы вы делали явное приведение. Таким образом, похоже, что компилятор действительно не знает, какой метод использовать. Поэтому он не знает, что делать для автоматического создания.

Вы можете просто использовать Task.Run<Int32>((Func<Int32>)MyIntReturningMethod), чтобы немного помочь. Хотя я согласен, что это похоже на компилятор, с которым можно справиться. Поскольку Func<Task<Int32>> не совпадает с Func<Int32>, поэтому не имеет смысла, что они путают компилятор.

4
задан Holt 17 January 2019 в 14:29
поделиться

1 ответ

clang (libc ++) кажется несовместимым по этому вопросу, потому что стандарт гласит:

[unique.ptr.single.dtor]

~unique_ptr();
  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 при уничтожении 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 сначала присваивает nullptr (pointer()), затем удаляет 1 .


1 pointer - это псевдоним, соответствующий Deleter::pointer, если он существует, или просто T*. ​​

0
ответ дан Holt 17 January 2019 в 14:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: