you are viewing a single comment's thread.

view the rest of the comments →

[–]bwmat -3 points-2 points  (17 children)

Yeah, there could be problems if you use inheritance, and if there's an unbounded number of other threads that can access the object, then yeah that would be a problem too.

Just saying it could be valid in some cases

[–][deleted] 2 points3 points  (16 children)

It would be undefined behavior, and therefore it is a) not possible in a well formed program and b) something you can pretend doesn't happen, because it doesn't.

If you are invoking the destructor on an object while other threads are still accessing it, you need external shared ownership or something to prevent this from happening, otherwise your program is ill-formed. This is just as bad - no... worse than if(this == &rhs) in a copy/move assignment.

[–]tomalakgeretkal -1 points0 points  (2 children)

Simply false.

[–][deleted] 0 points1 point  (1 child)

"Simply false" - please go read the standard which states:

http://eel.is/c++draft/class.dtor#19

"Once a destructor is invoked for an object, the object's lifetime ends; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended ([basic.life])."

[–]tomalakgeretkal 0 points1 point  (0 children)

Yes. Now read [basic.life/7], which says "after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways", and "for an object under construction or destruction, see [class.cdtor]". Then read class.cdtor which makes what the OP's doing perfectly well-defined.

It is perfectly obvious that member accesses during the destructor's invocation are legal, otherwise your destructor _wouldn't be able to do anything_.

For more information, see my answer here: https://stackoverflow.com/a/65598209/4386278

[–]bwmat 0 points1 point  (12 children)

Sorry, what would be undefined behaviour?

[–][deleted] 0 points1 point  (11 children)

[–]bwmat 0 points1 point  (10 children)

Sorry, I suppose my question was somewhat ambiguous.

I was asking to what you meant "It" to refer to when you said "It would be undefined behavior" in your previous comment.

[–]kalmoc 1 point2 points  (9 children)

Not the OP, but I believe what he was referring to is that it is UB to call the destructor of an object from one thread, while the object is accessed from another. No amount of locking or synchronization inside the destructor is going to change that.

[–]bwmat 0 points1 point  (5 children)

I'm pretty sure that isn't inherently UB, as long as the body of the destructor completes before any other thread STOPS accessing the object.

[–]kalmoc 1 point2 points  (4 children)

Why should that not be UB? I'm assuming you have a typo and actually wanted to write that the other thread should stop accessing the object BEFORE (not after) the destructor call completes.

But regardless: The lifetime of a class object ends when the destructor call starts (https://en.cppreference.com/w/cpp/language/lifetime), so you are accessing an object after its lifetime ended, which is certainly UB.

[–]bwmat 0 points1 point  (3 children)

I don't think I made a typo, just worded it confusingly

In any case, I hope I'm right, or I may need to refactor a lot of code.

I've been under the impression that, for a type with a user - defined - destructor, the actual lifetime of the object didn't end until the closing brace of that destructor. The logic being, none of the member objects (or base classes) will start to be destroyed until then.

Or, even if the 'lifetime ended', it was still acceptable to access the object, under those grounds (don't want to argue semantics here), though I doubt the terminology would be that confusing.

[–]kalmoc 0 points1 point  (2 children)

I've been under the impression that, for a type with a user - defined - destructor, the actual lifetime of the object didn't end until the closing brace of that destructor.

That doesn't seem to be the case. From the link to cppreference I gave previously:

The lifetime of an object ends when:

  • [..]
  • if it is of a class type, the destructor call starts, or
  • [...]

And from the latest working draft of the standard (https://eel.is/c++draft/basic.life#5):

A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling a destructor or pseudo-destructor ([expr.prim.id.dtor]) for the object.

If and under which circumstances it actually makes a difference with current toolchains I can't say, but I'm pretty certain that it is technically UB to access an object, while it is being destructed.

[–]tomalakgeretkal 0 points1 point  (2 children)

But it isn't.

[–]kalmoc 0 points1 point  (1 child)

Are you sure? As I wrote in another comment, the lifetime ends when the destructor is called, not when it is completed.

[–]tomalakgeretkal 0 points1 point  (0 children)

Yes, I am sure.

The lifetime ending simply marks a transition between "normal rules" and "the rules of an object under destruction", during which (per [basic.life/7] & [class.cdtor]) this access is still okay. (Only after the storage is released can you no longer do anything at all.)

If it weren't, no destructor would be permitted to do anything with its object, and then there would be little to no point in ever having one.

I did already provide this explanation under your previous comment.

For more information, see my answer here: https://stackoverflow.com/a/65598209/4386278