LINUX.ORG.RU

Использование объекта после вызова trivial destructor

 


0

4

Здравствуйте. Отмечу сразу, что знаю про всякие там промисы с фьючами. В структурах методы обычно не объявляю, для краткости, чтобы не париться с видимостью.

struct Q{
   struct T : std::thread{
      using std::thread::thread;
       ~T() { if(this->joinable()) this->join(); }
    }t;
   struct ITC{ std::atomic<bool> val; }itc;
   static void fn(ITC *) {/*while(ture); используем itc*/}
   Q(): t{fn, &itc} {}
};
Можно заметить - мы создаем Q, который запускает поток и создаёт область с разделяемыми данными между потоками itc. Судя по логике кода, деструктор itc будет вызван перед завершением порождённого потока, нехорошо, поток ведь в цикле будет работать со структурой. Но, структура ITC c trivial destructor, является ли это обстоятельством, которое допускает использование кода в таком виде (т.е. не париться о порядке создания/разрушения)? Если укоротить код, то можно так записать:
struct S{
   int i;
}s{3};
s.~S();
std::cout << s.i << '\n';  // допустимо ли использовать s учитывая то, что он trivial destructable?

★★

По тривальному деструктору у меня такие данные:

Trivial destructor
The destructor for class T is trivial if all of the following is true:
*The destructor is not user-provided (meaning, it is either implicitly declared, or explicitly defined as defaulted on its first declaration)
*The destructor is not virtual (that is, the base class destructor is not virtual)
*All direct base classes have trivial destructors
*All non-static data members of class type (or array of class type) have trivial destructors

A trivial destructor is a destructor that performs no action. Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage. All data types compatible with the C language (POD types) are trivially destructible.

Но как бы не про мой случай, вдруг там UB какое-нибудь.

pavlick ★★
() автор топика

Я не то чтобы гуру плюсов, но...

ИМХО, делать что-то с экземпляром класса после вызова его деструктора опасно хотя бы потому, что это контринтуитивно и приведёт к путанице.

Оффтопик: наследование от std::thread тоже не выглядит здравой идеей.

Deleted
()

Мне кажется, что поменять порядок объявления полей или отнаследоваться приватно от ITC будет меньшим извратом чем использовать объект после вызова деструктора.

xaizek ★★★★★
()

ТС, по-моему ты толсто троллишь.

Судя по логике кода, деструктор itc будет вызван перед завершением порождённого потока, нехорошо, поток ведь в цикле будет работать со структурой.

Ну так поменяй их местами, в чем проблема? Либо перепиши свой код с птичьего языка на человеческий.

asaw ★★★★★
()

Меня и самого смущает использование после деструктора, но тривиальный деструктор ведь должен быть аналогом сишного поведения. А какие там деструкторы? Контролировать порядок разрушения не проблема, конечно, но хочется понять. А следить за тривильностью деструктора не проблема:

struct Q{
   ...
   struct ITC{ std::atomic<bool> val; }itc;
   static_assert(std::is_trivially_destructible<ITC>::value, 
                 "ITC isn't trivial destructible");
   ...
};

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Потому что возможность наследования для него явно не обозначена (сравни с std::exception). Потому что у него нет виртуального деструктора (сравни с std::exception). Потому что авторы следующей версии стандарта добавят функцию std::thread::всё_сломать_нафиг() и юзеры твоего класса тут же её как-нибудь по-хитрому заюзают 8).

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted
#include <thread>
 
class Thread : std::thread
{
public:
   using std::thread::thread;
   using std::thread::native_handle;
};
 
int main()
{
    Thread t;
    std::thread *stdthread = &t; // error: 'std::thread' is an inaccessible base of 'Thread'
    t.native_handle(); // ok
    t.join(); // error: 'void std::thread::join()' is inaccessible within this context
}

К базовому не кастанёте из-за закрытого наследования, следовательно виртуальный деструктор и не нужен. А видимость функций контролируется через using.

pavlick ★★
() автор топика

Хм. Посмотрел в стандарте.

The lifetime of an object of type T ends when:
— if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
— the storage which the object occupies is reused or released.


Складывается ощущение, что можно.

Gvidon ★★★★
()

Почему бы itc просто не завернуть в shared_ptr и не мучаться?

Pavval ★★★★★
()

Зачем все эти навороты?

struct Q {
  struct ITC { std::atomic<bool> val; } itc{};
  std::thread t;
  void fn() { ... /* use itc */ }
  Q() : t( &Q::fn, this ) {}
  ~Q() { if(t.joinable()) t.join(); }
};

eao197 ★★★★★
()
Ответ на: комментарий от eao197

eao197, типа RAII, конструктор Q ведь может быть очень сложен (куча потоков, выделение памяти), думаю, разбить на этапы - правильней (выброшенное исключение в конструкторе Q корректно вызовет деструкторы у обвёрток над взятыми ресурсами, а у вас - нет).

Gvidon, спасибо. Читаю стандарт, отпишусь позже.

pavlick ★★
() автор топика

переход дороги после включения красного сигнала светофора

облизывание розетки после включения электричества

anonymous
()

Прочитал кучу инфы из стандарта, но какого-то однозначного ответа так и не нашёл. В одном месте:

Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended

а в другом:

For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.

Вторая цитата намекает, что объекты с нетривиальным деструктором можно использовать после деструктора.
Ну и ладно, не буду полагаться на это. перемещу itc выше t (я бы в любом случаи так сделал, но теперь сделаю это с большей уверенностью и пониманием)) ).

pavlick ★★
() автор топика

Кстати, для валидности первого кода, также необходим и trivial constructor (что не так, скорее всего). Правильней перенести запуск потока в тело конструктора.

ЗЫ: спасибо всем за участие.

pavlick ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.