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.

4322. Problematic Constraints on incomplete types in indirect and polymorphic

Section: 20.4 [mem.composite.types] Status: New Submitter: Jonathan B. Coe Opened: 2025-08-20 Last modified: 2025-08-28

Priority: Not Prioritized

View all issues with New status.

Discussion:

The class templates indirect<T> and polymorphic<T> allow the template argument T to be an incomplete type.

Both classes can be instantiated when the type T is incomplete: constraints are written so that requirements on incomplete types are not evaluated at class instantiation time.

For constructors with additional template parameters, there are currently constraints written on the potentially incomplete type T and the additional template parameters. Such constraints will not be evaluated at class instantiation time but could be explicitly evaluated in contexts where support for an incomplete T is required.

template<typename U>
class A {
  U u;
public:
  A(const SomeType&) requires std::is_constructible_v<U, SomeType> 
  {
    // […]
  }
};

when U is indirect<T> or polymorphic<T> for some type T, the existence of the requires clause will require that T is a complete type for constraints on indirect or polymorphic to be evaluated.

Constraints on T should be converted to Mandates on T so that constraint evaluation does not require T to be a complete type.

Proposed resolution:

This wording is relative to N5014.

  1. Modify 20.4.1.3 [indirect.ctor] as indicated:

    template<class U = T>
      constexpr explicit indirect(U&& u);
    

    -17- Constraints:

    1. (17.1) — is_same_v<remove_cvref_t<U>, indirect> is false,

    2. (17.2) — is_same_v<remove_cvref_t<U>, in_place_t> is false, and

    3. (17.3) — is_constructible_v<T, U> is true, and

    4. (17.4) — is_default_constructible_v<Allocator> is true.

    -?- Mandates: is_constructible_v<T, U> is true.

    -18- Effects: […]

    template<class U = T>
      constexpr explicit indirect(allocator_arg_t, const Allocator& a, U&& u);
    

    -19- Constraints:

    1. (19.1) — is_same_v<remove_cvref_t<U>, indirect> is false, and

    2. (19.2) — is_same_v<remove_cvref_t<U>, in_place_t> is false., and

    3. (19.3) — is_constructible_v<T, U> is true

    -?- Mandates: is_constructible_v<T, U> is true.

    -20- Effects: […]

    template<class... Us>
      constexpr explicit indirect(in_place_t, Us&&... us);
    

    -21- Constraints:

    1. (21.1) — is_constructible_v<T, Us...> is true, and

    2. (21.2) — is_default_constructible_v<Allocator> is true.

    -?- Mandates: is_constructible_v<T, Us...> is true.

    -22- Effects: […]

    template<class... Us>
      constexpr explicit indirect(allocator_arg_t, const Allocator& a,
                                  in_place_t, Us&& ...us);
    

    -23- ConstraintsMandates: is_constructible_v<T, Us...> is true

    -24- Effects: […]

    template<class I, class... Us>
      constexpr explicit indirect(in_place_t, initializer_list<I> ilist, Us&&... us);
    

    -25- Constraints:

    1. (25.1) — is_constructible_v<T, initializer_list<I>&, Us...> is true, and

    2. (25.2) — is_default_constructible_v<Allocator> is true.

    -?- Mandates: is_constructible_v<T, initializer_list<I>&, Us...> is true.

    -26- Effects: […]

    template<class I, class... Us>
      constexpr explicit indirect(allocator_arg_t, const Allocator& a,
                                  in_place_t, initializer_list<I> ilist, Us&&... us);
    

    -27- ConstraintsMandates: is_constructible_v<T, initializer_list<I>&, Us...> is true

    -28- Effects: […]

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

    template<class U = T>
      constexpr explicit polymorphic(U&& u);
    

    -12- Constraints: Where UU is remove_cvref_t<U>,

    1. (12.1) — is_same_v<UU, polymorphic> is false,

    2. (12.2) — derived_from<UU, T> is true,

    3. (12.3) — is_constructible_v<UU, U> is true,

    4. (12.4) — is_copy_constructible_v<UU> is true,

    5. (12.5) — UU is not a specialization of in_place_type_t, and

    6. (12.6) — is_default_constructible_v<Allocator> is true.

    -?- Mandates: derived_from<UU, T> is true.

    -13- Effects: […]

    template<class U = T>
      constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, U&& u);
    

    -14- Constraints: Where UU is remove_cvref_t<U>,

    1. (14.1) — is_same_v<UU, polymorphic> is false,

    2. (14.2) — derived_from<UU, T> is true,

    3. (14.3) — is_constructible_v<UU, U> is true,

    4. (14.4) — is_copy_constructible_v<UU> is true, and

    5. (14.5) — UU is not a specialization of in_place_type_t.

    -?- Mandates: derived_from<UU, T> is true.

    -15- Effects: […]

    template<class U, class... Ts>
      constexpr explicit polymorphic(in_place_type_t<U>, Ts&&... ts);
    

    -16- Constraints:

    1. (16.1) — is_same_v<remove_cvref_t<U>, U> is true,

    2. (16.2) — derived_from<U, T> is true,

    3. (16.3) — is_constructible_v<U, Ts> is true,

    4. (16.4) — is_copy_constructible_v<U> is true, and

    5. (16.5) — is_default_constructible_v<Allocator> is true.

    -?- Mandates: derived_from<U, T> is true.

    -17- Effects: […]

    template<class U, class... Ts>
      constexpr explicit polymorphic(allocator_arg_t, const Allocator& a,
                                     in_place_type_t<U>, Ts&&... ts);
    

    -18- Constraints:

    1. (18.1) — is_same_v<remove_cvref_t<U>, U> is true,

    2. (18.2) — derived_from<U, T> is true,

    3. (18.3) — is_constructible_v<U, Ts> is true, and

    4. (18.4) — is_copy_constructible_v<U> is true.

    -?- Mandates: derived_from<U, T> is true.

    -19- Effects: […]

    template<class U, class I, class... Us>
      constexpr explicit polymorphic(in_place_type_t<U>, initializer_list<I> ilist, Us&&... us);
    

    -20- Constraints:

    1. (20.1) — is_same_v<remove_cvref_t<U>, U> is true,

    2. (20.2) — derived_from<U, T> is true,

    3. (20.3) — is_constructible_v<U, initializer_list<I>&, Us...> is true,

    4. (20.4) — is_copy_constructible_v<U> is true, and

    5. (20.5) — is_default_constructible_v<Allocator> is true.

    -?- Mandates: derived_from<U, T> is true.

    -21- Effects: […]

    template<class U, class I, class... Us>
      constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, 
                                     in_place_type_t<U>, initializer_list<I> ilist, Us&&... us);
    

    -22- Constraints:

    1. (22.1) — is_same_v<remove_cvref_t<U>, U> is true,

    2. (22.2) — derived_from<U, T> is true,

    3. (22.3) — is_constructible_v<U, initializer_list<I>&, Us...> is true, and

    4. (22.4) — is_copy_constructible_v<U> is true.

    -?- Mandates: derived_from<U, T> is true.

    -23- Effects: […]