Date: Tue, 28 Oct 2025 16:50:15 +0000
On Tue, 28 Oct 2025 at 16:13, Nikl Kelbon <kelbonage_at_[hidden]> wrote:
> I dont see this as counter example. Its example of type, where move
> constructor does not guarantee... Any guarantees.
>
What? It guarantees that objects[this] will not be populated if the
constructor fails.
> So, its just correct behavior, where
>
> 1. variant not changed (still A)
> 2. B tried to construct and throws exception
>
The behaviour is not correct. I don't think you understood the example.
>
> I think compiler who writes such beaty code must expect exactly such
> behavior
>
No, because they expect that nobody will memcpy their objects around behind
their back, because the objects are not trivially copyable.
The point is that if this throws, and you memcpy the A object back into
place, the global registry of live objects has been modified. The "live" A
object is no longer in the map.
A simpler version would be:
std::map<void*, std::type_info> live_objects;
A::A() {
live_objects[this] = typeid(A);
}
~A::A() {
assert(live_objects.contains(this));
live_objects.erase(this);
}
B::B(B&&) {
// any other object at this address must have been destroyed already:
assert(!live_object.contains(this));
live_objects[this] = typeid(B);
}
Now you don't even need B::B(B&&) to throw.
You memcpy the live A object to the side buffer, then construct B in the
variant, and it fails the assertion, because the A object was not destroyed
before you reused the memory.
Your idea quite simply breaks the C++ object model. You just keep insisting
that it's OK, but it's not.
>
> вт, 28 окт. 2025 г. в 20:58, Jonathan Wakely via Std-Proposals <
> std-proposals_at_[hidden]>:
>
>>
>>
>> On Tue, 28 Oct 2025 at 15:04, Pavel Vazharov via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> On Tue, Oct 28, 2025 at 4:46 PM Nikl Kelbon via Std-Proposals <
>>> std-proposals_at_[hidden]> wrote:
>>> >
>>> > Thanks for the links, but I don't see any discussion of this
>>> particular idea there. Instead, they're discussing allocating the value on
>>> the heap or doubling the buffer.0308
>>> The first sentence in the https://wg21.link/P0308 says
>>> This paper argues in section III that when variant's contained types
>>> have noexcept move constructors, variant *shall* *never* be valueless,
>>> that is, the specification should *statically* guarantee that
>>> valueless_by_exception() will *never* return true.
>>>
>>>
>> Ah yes, but you see that paper didn't discuss the possibility of "just
>> make it work for all types by using magic" ;-)
>>
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
> I dont see this as counter example. Its example of type, where move
> constructor does not guarantee... Any guarantees.
>
What? It guarantees that objects[this] will not be populated if the
constructor fails.
> So, its just correct behavior, where
>
> 1. variant not changed (still A)
> 2. B tried to construct and throws exception
>
The behaviour is not correct. I don't think you understood the example.
>
> I think compiler who writes such beaty code must expect exactly such
> behavior
>
No, because they expect that nobody will memcpy their objects around behind
their back, because the objects are not trivially copyable.
The point is that if this throws, and you memcpy the A object back into
place, the global registry of live objects has been modified. The "live" A
object is no longer in the map.
A simpler version would be:
std::map<void*, std::type_info> live_objects;
A::A() {
live_objects[this] = typeid(A);
}
~A::A() {
assert(live_objects.contains(this));
live_objects.erase(this);
}
B::B(B&&) {
// any other object at this address must have been destroyed already:
assert(!live_object.contains(this));
live_objects[this] = typeid(B);
}
Now you don't even need B::B(B&&) to throw.
You memcpy the live A object to the side buffer, then construct B in the
variant, and it fails the assertion, because the A object was not destroyed
before you reused the memory.
Your idea quite simply breaks the C++ object model. You just keep insisting
that it's OK, but it's not.
>
> вт, 28 окт. 2025 г. в 20:58, Jonathan Wakely via Std-Proposals <
> std-proposals_at_[hidden]>:
>
>>
>>
>> On Tue, 28 Oct 2025 at 15:04, Pavel Vazharov via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> On Tue, Oct 28, 2025 at 4:46 PM Nikl Kelbon via Std-Proposals <
>>> std-proposals_at_[hidden]> wrote:
>>> >
>>> > Thanks for the links, but I don't see any discussion of this
>>> particular idea there. Instead, they're discussing allocating the value on
>>> the heap or doubling the buffer.0308
>>> The first sentence in the https://wg21.link/P0308 says
>>> This paper argues in section III that when variant's contained types
>>> have noexcept move constructors, variant *shall* *never* be valueless,
>>> that is, the specification should *statically* guarantee that
>>> valueless_by_exception() will *never* return true.
>>>
>>>
>> Ah yes, but you see that paper didn't discuss the possibility of "just
>> make it work for all types by using magic" ;-)
>>
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>>
>
Received on 2025-10-28 16:50:36
