3509. Range adaptor objects are underspecified

Section: 25.7.2 [range.adaptor.object] Status: Resolved Submitter: Tim Song Opened: 2020-12-15 Last modified: 2021-06-14

Priority: 2

View other active issues in [range.adaptor.object].

View all other issues in [range.adaptor.object].

View all issues with Resolved status.

Discussion:

There is implementation divergence in the handling of this example:

template<class F>
auto filter(F f) {
  return std::views::filter(f);
}
std::vector<int> v = {1, 2, 3, 4};
auto f = filter([i = std::vector{4}] (auto x) { return x == i[0]; });
auto x = v | f;

libstdc++'s views::filter stores a reference to the argument if it is passed as an lvalue, so f contains a dangling reference in the above example. MSVC's views::filter always stores a copy, and forwards that as either an lvalue or an rvalue depending on the range adaptor closure object's value category.

MSVC's behavior here seems desirable and is consistent with range-v3's behavior as well: the ability to form range adapter pipelines like this is an important feature, and there's nothing in the above example that even hints at a dangling reference problem.

However, the wording in 25.7.2 [range.adaptor.object] is unclear at best about exactly how the arguments are stored in the adaptor(args...) case; arguably, the requirement that adaptor(range, args...) be equivalent to adaptor(args...)(range) may even rule out the ability to make an extra copy. Certainly nothing in the wording guarantees that it is safe to reuse lvalue range pipelines (that is, v | f doesn't move from f).

[2021-01-15; Telecon prioritization]

Set priority to 2 following reflector and telecon discussions.

[2021-06-13 Resolved by the adoption of P2281R1 at the June 2021 plenary. Status changed: New → Resolved.]

Proposed resolution: