This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4532. Imprecise std::polymorphic wording seems to imply slicing

Section: 20.4.2.3 [polymorphic.ctor], 20.4.2.5 [polymorphic.assign], 20.4.2.4 [polymorphic.dtor] Status: New Submitter: Fraser Gordon Opened: 2026-02-27 Last modified: 2026-03-04

Priority: Not Prioritized

View all issues with New status.

Discussion:

The wording for some member functions of std::polymorphic is imprecise, leading to plausible interpretations that undermine the purpose of the type (i.e causing slicing on copy/move).

The wording on most copy/assign methods includes a clause "of type U, where U is the type of the owned object in other" but the copy/move assignment methods are missing this, which allows a reading where allocator_traits<Allocator>::construct is called to construct an instance of value_type instead of U.

A similar reading of the destructor effects would cause ~value_type() to be used instead of ~U(), so that should also be cleaned up.

Finally, the last sentence of the effects of one of the constructors (20.4.2.3 [polymorphic.ctor] p11) doesn't make sense in context ("constructs an object of type polymorphic, considering the owned object in other as an rvalue"). As written, this is creating a new polymorphic object and doing nothing with it when the intended effect would be to construct a new owned object.

Proposed resolution:

This wording is relative to N5032.

  1. Modify 20.4.2.3 [polymorphic.ctor] as indicated:

    constexpr polymorphic(allocator_arg_t, const Allocator& a, polymorphic&& other)
      noexcept(allocator_traits<Allocator>::is_always_equal::value);
    

    -11- Effects: alloc is direct-non-list-initialized with a. If other is valueless, *this is valueless. Otherwise, if alloc == other.alloc is true, either constructs an object of type polymorphic that owns the owned object of other, making other valueless; or, owns an object of the same type constructed from the owned object of other considering that owned object as an rvalue. Otherwise, if alloc != other.alloc is true, constructs an object of type polymorphic, consideringowned object of type U, where U is the type of the owned object in other, with the owned object in other as an rvalue, using the allocator alloc.

  2. Modify 20.4.2.5 [polymorphic.assign] as indicated:

    constexpr polymorphic& operator=(const polymorphic& other);
    

    -1- Mandates: T is a complete type.

    -2- Effects: If addressof(other) == this is true, there are no effects. Otherwise:

    • (2.1) — […]

    • (2.2) — If other is not valueless, a new owned object of type U, where U is the type of the owned object in other, is constructed in *this using allocator_traits<Allocator>::construct with the owned object from other as the argument, using either the allocator in *this or the allocator in other if the allocator needs updating.

    • (2.3) — […]

    • (2.4) — […]

    constexpr polymorphic& operator=(polymorphic&& other)
      noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value ||
               allocator_traits<Allocator>::is_always_equal::value);
    

    -5- Mandates: If allocator_traits<Allocator>::propagate_on_container_move_assignment::value is false and allocator_traits<Allocator>::is_always_equal::value is false, T is a complete type.

    -6- Effects: If addressof(other) == this is true, there are no effects. Otherwise:

    • (6.1) — […]

    • (6.2) — […]

    • (6.3) — […]

    • (6.4) — Otherwise, constructs a new owned object of type U, where U is the type of the owned object in other, with the owned object of other as the argument as an rvalue, using the allocator in *this.

    • (6.5) — […]

    • (6.6) — […]

  3. Modify 20.4.2.4 [polymorphic.dtor] as indicated:

    constexpr ~polymorphic();
    

    -1- Mandates: T is a complete type.

    -2- Effects: If *this is not valueless, destroys the owned object using allocator_traits<Allocator>::destroy andcalls allocator_traits<Allocator>::destroy(p), where p is a pointer of type U* to the owned object and U is the type of the owned object; then the storage is deallocated.