Revised 2026-06-04 at 20:53:02 UTC

Tentative Issues


4121(i). ranges::to constructs associative containers via c.emplace(c.end(), *it)

Section: 25.5.7.1 [range.utility.conv.general] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-07-16 Last modified: 2026-05-29

Priority: 2

View all issues with Tentatively Ready status.

Discussion:

When ranges::to constructs an associative container, if there is no range version constructor for C and the input range is not common range, ranges::to will dispatch to the bullet 25.5.7.2 [range.utility.conv.to] (2.1.4), which first default-constructs the container and emplaces the element through c.emplace(c.end(), *it).

However, this is not the correct way to call emplace() on an associative container as it does not expect an iterator as the first argument, and since map::emplace(), for instance, is not constrained, which turns out a hard error because we are trying to make a pair with {it, pair}.

Given that libstdc++ currently does not implement the range constructor for associative containers, the following illustrates the issue:

#include <ranges>
#include <set>
  
auto s = std::views::iota(0)
       | std::views::take(5)
       | std::ranges::to<std::set>(); // hard error

The proposed resolution simply removes the emplace() branch. Although this means that we always use insert() to fill associative containers, such an impact seems negligible.

[2024-07-23; This was caused by LWG 4016(i).]

[2024-08-02; Reflector poll]

Set priority to 2 after reflector poll. "Would like to preserve the ability to use emplace. Tim suggested trying emplace_hint first, then emplace." "I tried it, it gets very verbose, because we might also want to try insert(*it) instead of insert(c.end(), *it) if emplace(*it) is not valid for associative containers, because c.end() might not be a good hint." "It might be suboptimal, but it still works."

Previous resolution [SUPERSEDED]

This wording is relative to N4986.

  1. Modify 25.5.7.1 [range.utility.conv.general] as indicated:

    -4- Let container-appendable be defined as follows:

    template<class Container, class Ref>
    constexpr bool container-appendable =         // exposition only
      requires(Container& c, Ref&& ref) {
               requires (requires { c.emplace_back(std::forward<Ref>(ref)); } ||
                         requires { c.push_back(std::forward<Ref>(ref)); } ||
                         requires { c.emplace(c.end(), std::forward<Ref>(ref)); } ||
                         requires { c.insert(c.end(), std::forward<Ref>(ref)); });
    };
    

    -5- Let container-append be defined as follows:

    template<class Container>
    constexpr auto container-append(Container& c) {     // exposition only
      return [&c]<class Ref>(Ref&& ref) {
        if constexpr (requires { c.emplace_back(declval<Ref>()); })
          c.emplace_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.push_back(declval<Ref>()); })
          c.push_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.emplace(c.end(), declval<Ref>()); })
          c.emplace(c.end(), std::forward<Ref>(ref));
        else
          c.insert(c.end(), std::forward<Ref>(ref));
      };
    };
    

[2025-03-13; Jonathan provides improved wording for Tim's suggestion]

It's true that for some cases it might be optimal to use c.emplace(ref) instead of c.emplace_hint(c.end(), ref) but I don't think I care. A bad hint is not expensive, it's just an extra comparison then the hint is ignored. And this code path isn't going to be used for std::set or std::map, only for user-defined associative containers that don't have a from_range_t constructor or a C(Iter,Sent) constructor. I think just fixing the original issue is all we need, rather than trying to handle every possible way to insert elements.

This is a simpler, portable reproducer that doesn't depend on the current implementation status of std::set in libstdc++:


#include <ranges>
#include <set>
struct Set : std::set<int> {
  Set() { }; // No other constructors
};
int main() {
  int a[1];
  auto okay = std::ranges::to<std::set<int>>(a);
  auto ohno = std::ranges::to<Set>(a);
}
Previous resolution [SUPERSEDED]

This wording is relative to N5001.

  1. Modify 25.5.7.1 [range.utility.conv.general] as indicated:

    -4- Let container-appendable be defined as follows:

    template<class Container, class Ref>
    constexpr bool container-appendable =         // exposition only
      requires(Container& c, Ref&& ref) {
               requires (requires { c.emplace_back(std::forward<Ref>(ref)); } ||
                         requires { c.push_back(std::forward<Ref>(ref)); } ||
                         requires { c.emplace_hint(c.end(), std::forward<Ref>(ref)); } ||
                         requires { c.emplace(c.end(), std::forward<Ref>(ref)); } ||
                         requires { c.insert(c.end(), std::forward<Ref>(ref)); });
    };
    

    -5- Let container-append be defined as follows:

    template<class Container>
    constexpr auto container-append(Container& c) {     // exposition only
      return [&c]<class Ref>(Ref&& ref) {
        if constexpr (requires { c.emplace_back(declval<Ref>()); })
          c.emplace_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.push_back(declval<Ref>()); })
          c.push_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.emplace_hint(c.end(), declval<Ref>()); })
          c.emplace_hint(c.end(), std::forward<Ref>(ref));
        else if constexpr (requires { c.emplace(c.end(), declval<Ref>()); })
          c.emplace(c.end(), std::forward<Ref>(ref));
        else
          c.insert(c.end(), std::forward<Ref>(ref));
      };
    };
    

[2026-05-22; Jonathan provides new wording]

Hewill noted that all the standard sequence containers that could use c.emplace(c.end(), ref) would already have used the emplace_back branch, so there's no point using c.emplace. Just change that branch to use emplace_hint for associative containers.

[2026-05-29; Reflector poll.]

Set status to Tentatively Ready after nine votes in favour during reflector poll.

Proposed resolution:

This wording is relative to N5046.

  1. Modify 25.5.7.1 [range.utility.conv.general] as indicated:

    -4- Let container-appendable be defined as follows:

    template<class Container, class Ref>
    constexpr bool container-appendable =         // exposition only
      requires(Container& c, Ref&& ref) {
               requires (requires { c.emplace_back(std::forward<Ref>(ref)); } ||
                         requires { c.push_back(std::forward<Ref>(ref)); } ||
                         requires { c.emplace_hint(c.end(), std::forward<Ref>(ref)); } ||
                         requires { c.insert(c.end(), std::forward<Ref>(ref)); });
    };
    

    -5- Let container-append be defined as follows:

    template<class Container>
    constexpr auto container-append(Container& c) {     // exposition only
      return [&c]<class Ref>(Ref&& ref) {
        if constexpr (requires { c.emplace_back(declval<Ref>()); })
          c.emplace_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.push_back(declval<Ref>()); })
          c.push_back(std::forward<Ref>(ref));
        else if constexpr (requires { c.emplace_hint(c.end(), declval<Ref>()); })
          c.emplace_hint(c.end(), std::forward<Ref>(ref));
        else
          c.insert(c.end(), std::forward<Ref>(ref));
      };
    };
    

4417(i). views::indices is underconstrained

Section: 25.6.4.1 [range.iota.overview] Status: Tentatively NAD Submitter: Hewill Kang Opened: 2025-10-15 Last modified: 2026-01-16

Priority: Not Prioritized

View all other issues in [range.iota.overview].

View all issues with Tentatively NAD status.

Discussion:

Whether an integer-class type satisfies weakly_incrementable is unspecified according to 25.6.4.2 [range.iota.view]. For example, the library may provide a member type alias difference_type for the integer-class type to make it weakly_incrementable, or not.

If the latter, then views::iota(integer-class-type(0)) is always ill-formed because iota_view<W, Bound> requires W to be weakly_incrementable.

However, unlike views::iota, views::indices unconditionally accepts integer-class types, which will lead to a hard error in the function body if the integer-class type is not weakly_incrementable, which is true for both libstdc++ and MSVC-STL.

[2026-01-16; Reflector poll. Status → Tentatively NAD.]

"This is implied by the expression-equivalence."

Proposed resolution:

This wording is relative to N5014.

  1. Modify 25.6.4.1 [range.iota.overview] as indicated:

    -4- The name views::indices denotes a customization point object (16.3.3.3.5 [customization.point.object]). Given subexpression E, let T be remove_cvref_t<decltype((E))>. views::indices(E) is expression-equivalent to views::iota(T(0), E) if is-integer-like<T> is true and T models weakly_incrementable, and ill-formed otherwise.


4475(i). The description to single total order in [thread.mutex.requirements.mutex.general] is hollow

Section: 32.6.4.2.1 [thread.mutex.requirements.mutex.general] Status: Tentatively NAD Submitter: jim x Opened: 2025-11-14 Last modified: 2026-01-16

Priority: Not Prioritized

View other active issues in [thread.mutex.requirements.mutex.general].

View all other issues in [thread.mutex.requirements.mutex.general].

View all issues with Tentatively NAD status.

Discussion:

32.6.4.2.1 [thread.mutex.requirements.mutex.general] p4 says:

For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). The lock and unlock operations on a single mutex appears to occur in a single total order.

Even for atomic operations, the precondition for ordering them in a single total order is that they must be memory_order::seq_cst operations, such that we can form the total order to reason.

Put aside the fact that we impose the preconditions on unlock and lock. Is this a possible total order if lock reads unlock_1, but there is a unlock_2 between them

unlock_1 < unlock_2 < lock

First, although we have said that lock and unlock operations behave as atomic operations, and lock reads unlock_1, meaning that unlock_1 is coherence-ordered before lock, however, we don't specify that they are memory_order::seq_cst operations, so 32.5.4 [atomics.order] p4 doesn't apply here

Second, for every pair of atomic operations A and B on an object M, where A is coherence-ordered before B, the following four conditions are required to be satisfied by S:

So, it is not helpful to decide that unlock_1 precedes lock in a single total order. Similarly, excluding unlock_1 < unlock_2 < lock is not possible.

Suggested resolution:

The lock and unlock operations on a single mutex appears to occur in a single total order; for this purpose, these operations are considered as memory_order::seq_cst operations

[2026-01-16; Reflector poll. Status → Tentatively NAD.]

For atomic objects, the modification order is already a single total order, seq_cst or not. This isn't a useful change.

Proposed resolution:

This wording is relative to N5014.

  1. Modify 32.6.4.2.1 [thread.mutex.requirements.mutex.general] as indicated:

    -4- The implementation provides lock and unlock operations, as described below. For purposes of determining the existence of a data race, these behave as atomic operations (6.10.2 [intro.multithread]). The lock and unlock operations on a single mutex appears to occur in a single total order; for this purpose, these operations are considered as memory_order::seq_cst operations.


4494(i). Add consteval to std::meta::exception defaulted member functions

Section: 21.4.4 [meta.reflection.exception] Status: Tentatively NAD Submitter: Marek Polacek Opened: 2025-12-16 Last modified: 2026-01-28

Priority: Not Prioritized

View other active issues in [meta.reflection.exception].

View all other issues in [meta.reflection.exception].

View all issues with Tentatively NAD status.

Discussion:

CWG 3115 states that every function of consteval-only type shall be an immediate function. This caused a problem for std::meta::exception::what() which was marked constexpr but not consteval. To be able to mark it consteval, we had to tweak the rules about overriding by a consteval virtual function (CWG 3117).

But 21.4.4 [meta.reflection.exception] still defines std::meta::exception such that it contains these defaulted special member functions:

exception(const exception&) = default;
exception(exception&&) = default;

exception& operator=(const exception&) = default;
exception& operator=(exception&&) = default;

which aren't consteval (and since they're not templates, they won't be promoted to consteval as per P2564). I propose to make the four functions consteval:

consteval exception(const exception&) = default;
consteval exception(exception&&) = default;

consteval exception& operator=(const exception&) = default;
consteval exception& operator=(exception&&) = default;

[2026-01-28; Reflector poll. Status → Tentatively NAD.]

The second change in CWG 3115 fixes this.

Proposed resolution:

This wording is relative to N5032.

  1. Modify 21.4.4 [meta.reflection.exception] as indicated:

    namespace std::meta {
      class exception : public std::exception {
      private:
        optional<string> what_;  // exposition only
        u8string u8what_;        // exposition only
        info from_;              // exposition only
        source_location where_;  // exposition only
      public:
        consteval exception(u8string_view what, info from,
                            source_location where = source_location::current()) noexcept;
                            
        consteval exception(string_view what, info from,
                            source_location where = source_location::current()) noexcept;
                            
        consteval exception(const exception&) = default;
        consteval exception(exception&&) = default;
        
        consteval exception& operator=(const exception&) = default;
        consteval exception& operator=(exception&&) = default;
        
        constexpr const char* what() const noexcept override;
        consteval u8string_view u8what() const noexcept;
        consteval info from() const noexcept;
        consteval source_location where() const noexcept;
      };
    }
    

4531(i). Should there be a feature-test macro update for constexpr std::to_(w)string?

Section: 17.3.2 [version.syn] Status: Tentatively Ready Submitter: Jiang An Opened: 2026-02-25 Last modified: 2026-05-22

Priority: 2

View other active issues in [version.syn].

View all other issues in [version.syn].

View all issues with Tentatively Ready status.

Discussion:

P3391R2 added constexpr to integral overloads of std::to_string and std::to_wstring. However, the paper only added one feature-test macro __cpp_lib_constexpr_format, which doesn't seem clearly related to std::to_(w)string.

Given that P3391R2 contained the constexpr additions in P3438R0 which proposed to bump __cpp_lib_to_string, perhaps we should bump that FTM. It might be also reasonable to bump __cpp_lib_constexpr_string for pure constexpr additions.

[2026-04-13; Reflector poll.]

Set priority to 2 after reflector poll.

Previous resolution [SUPERSEDED]

This wording is relative to N5032.

[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A:

  1. Modify 17.3.2 [version.syn] as indicated:

    […]
    #define __cpp_lib_to_string                         202306L202511L // also in <string>
    […]
    

Option B:

  1. Modify 17.3.2 [version.syn] as indicated:

    […]
    #define __cpp_lib_constexpr_string                  201907L202511L // also in <string>
    […]
    

[2026-05-11; Select single resolution.]

Update wording to option B, that had slight preference in reflector discussion.

[2026-05-22; Reflector poll.]

Set status to Tentatively Ready after six votes in favour during reflector poll.

Proposed resolution:

This wording is relative to N5032.

  1. Modify 17.3.2 [version.syn] as indicated:

    […]
    #define __cpp_lib_constexpr_string                  201907L202511L // also in <string>
    […]
    

4542(i). Resuming a coroutine on a different execution agent that is an instance of a std::thread is underspecified

Section: 17.13.4.6 [coroutine.handle.resumption] Status: Tentatively NAD Submitter: jim x Opened: 2026-03-11 Last modified: 2026-04-13

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

Consider the following example:

#include <atomic>
#include <cassert>
#include <coroutine>
#include <thread>

std::atomic<bool> flag = false;
int global = 0;

struct Task 
{
  struct promise_type 
  {
    Task get_return_object() {
      return {std::coroutine_handle<promise_type>::from_promise(*this)};
    }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
    void return_void() {}
    void unhandled_exception() {}
  };

  std::coroutine_handle<promise_type> handle;

  Task(std::coroutine_handle<promise_type> h) : handle(h) {}
  ~Task() {}
};

struct SuspendOnce 
{
  bool suspended = false;
  bool await_ready() { return suspended; }
  void await_suspend(std::coroutine_handle<>) { suspended = true; }
  void await_resume() {}
};

Task my_coroutine() {
  co_await SuspendOnce{};
  flag.store(true, std::memory_order::release); // #2
}

auto t1 = std::thread([]() {
  while (!flag.load(std::memory_order::acquire)); //#3
  assert(global == 1); // #4
});

int main() {
  auto task = my_coroutine();
  auto& h = task.handle;
  auto t2 = std::thread([&]() {
    global = 1; // #1
    h.resume();
  });
  t2.join();
  t1.join();
}

17.13.4.6 [coroutine.handle.resumption] p1 says:

Resuming a coroutine via resume, operator(), or destroy on an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance of std::thread or std::jthread, or is the thread that executes main.

The wording doesn't specify the behavior for the latter case. We want #1 to be sequenced before #2.

The sequenced-before is described in terms of two evaluations that are both executed by a single thread. The coroutine is initially executed in the main thread; however, it's resumed in a different thread. The first paragraph doesn't specify the specific behavior; it only says the behavior is implementation-defined if the different execution agent is not an instance of std::thread or std::jthread, or is the thread that executes main.

[2026-04-13; Reflector poll. Status → Tentatively NAD.]

The point of paragraph 1 is to say when paragraphs 2-5 don't apply (e.g. if you suspend on the main thread and resume on a fiber). If you don't do anything like that, then paragraphs 2-5 describe the effects.

Proposed resolution:

This wording is relative to N5032.

  1. Modify 17.13.4.6 [coroutine.handle.resumption] as indicated:

    -1- Resuming a coroutine via resume, operator(), or destroy on an execution agent other than the one on which it was suspended has implementation-defined behavior unless each execution agent either is an instance of std::thread or std::jthread, or is the thread that executes main. Otherwise, the execution of the coroutine is resumed, or the coroutine is destroyed, on the execution agent on which the corresponding member function is invoked.

    […]


4567(i). Feature test macro value for apply_result, is_applicable

Section: 17.3.2 [version.syn] Status: Tentatively Ready Submitter: Tomasz Kamiński Opened: 2026-04-08 Last modified: 2026-05-30

Priority: Not Prioritized

View other active issues in [version.syn].

View all other issues in [version.syn].

View all issues with Tentatively Ready status.

Discussion:

The paper P1317R2 "Remove return type deduction in std::apply", which changed the definition of std::apply and introduced the new traits is_applicable, is_nothrow_applicable, and apply_result, was accepted in Sofia.

In Croydon we moved the P3795R2 "Miscellaneous Reflection Cleanup", that adds the reflection equivalents of above, under __cpp_lib_reflection.

This leads to situation, when an implementation that does not yet implement P1317R2, and thus does not define the corresponding meta functions, should not define __cpp_lib_reflection.

I propose to assign the following __cpp_lib_apply macro values to account to above papers:

The proposed resolution for the standard working paper represents only the effective end state.

[2026-05-29; Reflector poll.]

Set status to Tentatively Ready after six votes in favour during reflector poll.

Proposed resolution:

This wording is relative to N5032.

  1. Modify 17.3.2 [version.syn] as indicated:

    #define __cpp_lib_apply 202506L202603L // freestanding, also in <tuple>, <type_traits>, <meta>
    

4568(i). std::execution::spawn_future is mishandling dependent senders

Section: 33.9.12.18 [exec.spawn.future] Status: Tentatively Ready Submitter: Eric Niebler Opened: 2026-04-11 Last modified: 2026-05-30

Priority: Not Prioritized

View all other issues in [exec.spawn.future].

View all issues with Tentatively Ready status.

Discussion:

In 33.9.12.18 [exec.spawn.future] p7, we see this:

-7- Let spawn-future-state be the exposition-only class template:

namespace std::execution {
  template<class Alloc, scope_token Token, sender Sender, class Env>
  struct spawn-future-state                   // exposition only
    : spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {
    using sigs-t =                            // exposition only
      completion_signatures_of_t<future-spawned-sender<Sender, Env>>;
    using receiver-t =                        // exposition only
      spawn-future-receiver<sigs-t>;
    using op-t =                              // exposition only
      connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
  […]
  };
[…]
}

where spawn-future-sender<Sender, Env> is the type of write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()).

The problem happens in the definition of sigs-t. There is a difference between asking for a sender's completion signatures with an empty environment, and asking with no environment.

In sigs-t, we are asking for the completion signatures with no environment. That is asking the sender for its non-dependent completions. But if Sender is a dependent sender, then so is future-spawned-sender<Sender, Env>, and so this line will not compile. A dependent sender cannot provide non-dependent completion signatures.

The spawn_future algorithm connects the future-spawned-sender with a receiver that has an empty environment. Therefore, we should be using the empty environment when computing the future-spawned-sender's completion signatures.

[2026-05-29; Reflector poll.]

Set status to Tentatively Ready after five votes in favour during reflector poll.

Proposed resolution:

This wording is relative to N5032.

  1. Modify 33.9.12.18 [exec.spawn.future] as indicated:

    -7- Let spawn-future-state be the exposition-only class template:

    namespace std::execution {
      template<class Alloc, scope_token Token, sender Sender, class Env>
      struct spawn-future-state                   // exposition only
        : spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>, env<>>> {
        using sigs-t =                            // exposition only
          completion_signatures_of_t<future-spawned-sender<Sender, Env>, env<>>;
        using receiver-t =                        // exposition only
          spawn-future-receiver<sigs-t>;
        using op-t =                              // exposition only
          connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
      […]
      };
    […]
    }
    

4579(i). make-state<Rcvr>::state-type::complete uses Env which is not in scope

Section: 33.9.12.12 [exec.when.all] Status: Tentatively Ready Submitter: Abhinav Agarwal Opened: 2026-05-12 Last modified: 2026-05-30

Priority: Not Prioritized

View other active issues in [exec.when.all].

View all other issues in [exec.when.all].

View all issues with Tentatively Ready status.

Discussion:

In 33.9.12.12 [exec.when.all], the specification of make-state::state-type::complete (paragraph 15, bullet (15.3)) defines sends-stopped as true if and only if there exists an element S of Sndrs such that

completion_signatures_of_t<S, when-all-env<Env>>

contains set_stopped_t().

However, no declaration named Env is in scope at this point. state-type is specified in the context of make-state<Rcvr>::operator()<Sndrs...>; the relevant named template parameters there are Rcvr and Sndrs.... Env is a template parameter of check-types<Sndr, Env...>, which is a separate member function whose scope does not extend into make-state.

Other environment-dependent wording within the make-state scope uses env_of_t<Rcvr>:

The sends-stopped definition in bullet (15.3) is the only use of bare Env in this scope.

This issue was introduced by P3887R1 ("Make when_all a Ronseal Algorithm"), whose wording contains the same text.

[2026-05-29; Reflector poll.]

Set status to Tentatively Ready after five votes in favour during reflector poll.

Proposed resolution:

This wording is relative to N5046.

  1. Modify 33.9.12.12 [exec.when.all] as indicated:

    -15- The member void state-type::complete(Rcvr& rcvr) noexcept behaves as follows:

    1. (15.1) — If disp is equal to disposition::started, evaluates: […]

    2. (15.2) — Otherwise, if disp is equal to disposition::error, evaluates: […]

    3. (15.3) — Otherwise, evaluates:

      if constexpr (sends-stopped) {
        on_stop.reset();
        set_stopped(std::move(rcvr));
      }
      

      where sends-stopped equals true if and only if there exists an element S of Sndrs such that completion_signatures_of_t<S, when-all-env<Envenv_of_t<Rcvr>>> contains set_stopped_t().