Skip to content

VLA move construction with unequal allocators should preserve source capacity #211

Description

@thirtytwobits

Summary

Change allocator-extended move construction with unequal allocators to empty the source while preserving its buffer and capacity. This should match the existing unequal-allocator move-assignment behaviour.

The distinction should be whether ownership of the source allocation transfers. When storage is transferred, the source necessarily loses its capacity. When elements are relocated into storage owned by another allocator, the source should retain its allocation for reuse. Callers that want to release it can explicitly call shrink_to_fit() after the successful move.

Current behaviour

Operation Source after successful move
Move construction storage transferred, capacity() == 0
Allocator-extended move construction, equal allocators storage transferred, capacity() == 0
Allocator-extended move construction, unequal allocators storage deallocated, capacity() == 0
Move assignment, POCMA or always-equal storage transferred, capacity() == 0
Move assignment, non-POCMA, equal at runtime storage transferred, capacity() == 0
Move assignment, non-POCMA, unequal at runtime source emptied, storage kept, capacity() unchanged

Both unequal-allocator branches of move_assign_from finish with rhs.resize(0, rhs_max_size), destroying the elements while retaining the source allocation:

The allocator-extended move constructor instead deallocates the source buffer. Change the constructor to retain it; keep the assignment behaviour.

Rationale

This is a resource-management policy decision, not a correctness bug.

Relocation between unequal allocators requires the source buffer and destination storage to coexist while the elements are transferred. For construction, if the source buffer occupies S bytes and the destination buffer needs D bytes, both policies reach S + D bytes of container storage during relocation. Deallocating the source after relocation cannot reduce that peak.

Releasing the source does reduce retained memory after the operation, making its arena available for later allocations. Retaining it instead permits source reuse without another allocation. Both are useful in embedded systems, so callers should be able to choose. A caller can explicitly release retained storage, but cannot undo automatic deallocation: reserving the old capacity again requires allocator work and may fail.

Callers that want reclamation can use the same sequence regardless of allocator equality:

VLA destination(std::move(source), destination_allocator);
source.shrink_to_fit();

Under the proposed successful-move contract, the source is already empty. CETL's empty-container shrink_to_fit() directly deallocates retained storage without allocating a replacement, and is a no-op when capacity is already zero. This release behaviour should be documented for CETL; std::vector::shrink_to_fit() is only a nonbinding request.

Proposed change

  1. In allocator-extended move construction with unequal allocators, destroy the source elements only after destination construction succeeds, set the source size to zero, and retain its data pointer, capacity, and allocator. Keep the existing storage-transfer behaviour when allocators compare equal. Ensure the bool specialization resets the source's last_byte_bit_fill_ consistently.
  2. Keep source capacity retention in both unequal-allocator move-assignment branches. Replace the TODO with a comment explaining the deliberate retention policy.
  3. Document the successful-move contract: storage-transfer paths leave the source empty with zero capacity; unequal-allocator relocation paths leave the source empty with its original capacity. Document explicit shrink_to_fit() for callers wanting release. Do not extend these successful-move postconditions to failed operations.
  4. Update move-construction tests in test_variable_length_array_move_capacity.cpp and add corresponding move-assignment coverage. Cover nonempty and empty-but-reserved sources, both assignment capacity branches, and ordinary elements and packed bool. Verify retained source allocation ownership and capacity, allocation-free source reuse within that capacity, and explicit release by shrink_to_fit(). Keep equal-allocator storage-transfer checks and verify shrinking an already zero-capacity source is harmless.
  5. Preserve the destination allocation accounting fixed by VLA unequal-allocator move constructor records source capacity for a smaller allocation #205 / Fix capacity of VLA moves with unequal allocators #208. Retaining capacity in the source does not mean allocating the source's spare capacity in the destination: unequal-allocator construction should still allocate only the storage required for the transferred elements, including packed-bool rounding.

Separate destination-buffer policy and exception guarantees

Keep the existing unequal-allocator move-assignment policy of freeing an insufficient destination buffer before allocating its replacement. Unlike releasing the source after relocation, this avoids keeping the old destination buffer alive alongside the source and replacement buffers, reducing the additional peak storage required during replacement.

Document the resulting exception guarantees separately. In the reallocate branch, failure during replacement allocation or element construction leaves the destination valid but empty rather than unchanged. The fits-in-capacity branch can leave destination elements partially updated if element assignment throws. Throwing element moves may also modify source values. Source-capacity retention does not imply a strong exception guarantee.

The move_assign_alloc calls in the unequal-allocator assignment branches are always no-ops because POCMA is false. Their conditional wording can be corrected, or the calls removed, in the same change.

Related

Activity

  1. changed the title [-]VLA move assignment with unequal allocators should release the source's storage[/-] [+]VLA move construction with unequal allocators should preserve source capacity[/+] on Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedThis is an accepted item to be implemented.component/VLAvariable_length_array issues

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions