Full name of submitter (unless configured in github; will be published with the issue): Liu Yanzuo
Reference (section label):[stmt.expand]
Link to reflector thread (if any): https://lists.isocpp.org/std-discussion/2026/01/3275.php
Issue description:
Right now, the wording for a destructuring expansion statement says that we create a possibly-constexpr structured binding declaration
auto&& [u_0, u_1, ..., u_N-1] = expansion_initializer;
And then each statement of the expansion statement is:
for_range_declaration = u_i;
As worded, u_i is always an lvalue. Which means that in an example like this:
auto f() -> std::tuple<int, int&, int&&>;
auto g() -> void {
template for (auto&& elem : f()) {
;
}
}
decltype(elem) is int& on each iteration. But the intent was that we would get int&& on the first and third iteration. This was never quite translated properly to the wording.
Suggested resolution: Something to the effect of:
for-range-declaration = ui get-expri;
Where get-expri is ui if either decltype(ui) is an lvalue reference type or expansion-initializer is an lvalue. Otherwise, std::move(ui).
Full name of submitter (unless configured in github; will be published with the issue): Liu Yanzuo
Reference (section label):[stmt.expand]
Link to reflector thread (if any): https://lists.isocpp.org/std-discussion/2026/01/3275.php
Issue description:
Right now, the wording for a destructuring expansion statement says that we create a possibly-constexpr structured binding declaration
And then each statement of the expansion statement is:
As worded,
u_iis always an lvalue. Which means that in an example like this:decltype(elem)isint&on each iteration. But the intent was that we would getint&&on the first and third iteration. This was never quite translated properly to the wording.Suggested resolution: Something to the effect of:
for-range-declaration =get-expri;uiWhere
get-expriisuiif eitherdecltype(ui)is an lvalue reference type orexpansion-initializeris an lvalue. Otherwise,std::move(ui).