P3817 — process

◀ back to the paper

What R1 changed

How the proposal moved between revisions

The motivation got concrete

R0 argued from principle: some environments do not have the standard library. R1 found a place where the standard exists but is not allowed.

Fuchsia’s Zircon kernel bans std in-kernel. This is real code from it, assigning a loop-scoped binding out to a longer-lived variable:

Today

auto [fault_status, _] =
    mapping->PageFaultLocked(guest_paddr, pf_flags, 0, &page_request);
status = fault_status;

With P3817

auto [using status, _] =
    mapping->PageFaultLocked(guest_paddr, pf_flags, 0, &page_request);

The specifier rules were reversed

R0 made const, constexpr, static, thread_local and constinit ill-formed with a using-marked element. R1 says the opposite: all of them are well-formed.

An innocent feedback question about static led to deeper investigation →

Then the understanding that they all refer only to the hidden variable →

Then the understanding that all specifiers stand and fall together →

Then the understanding that diverting from today’s specification just for a using-marked element is wrong →

Then everything became simpler.

So we wrote the wording

The Wording section was a placeholder: “Will be completed upon positive response on the direction of the proposal”.

The feedback was to write it anyway, on the grounds that specifying a design reveals things that reviewing it does not.

It was right

Rules that read well did not survive being written down

The duplicate ban was dropped

A rule forbidding the same target twice was drafted and then removed, because it could not be written.

Unenforceable

int glob;
int& x = glob;
int& y = glob;
int& f() {return glob;}
int& g() {return glob;}
std::pair<int,int> bar();
auto [using x, using y] = bar();      // two names, one object
auto [using f(), using g()] = bar();  // may return the same object

The order rule makes the result well-defined: the last assignment wins.

A target may reuse a declared name

Already handled

struct Counter {
    int count = 0;                                  // the member
    std::pair<int, int> tick();                     // {delta, running_total}
    int step() {
        auto [count, using count] = tick();
        //    │             └── this is the MEMBER — the binding to its
        //    └── the binding    left is not yet in scope at this point

        use(count);          // the binding — first element (delta)
        return this->count;  // the member  — second element (running_total)
    }
};