[range] Question about adapted range safety given C++11 auto

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3350.html claims: "Boost's range library focuses on defining a set of Range concepts that allow Containers to be Ranges. Because Containers are heavy-weight, this forces Boost to use references for all of their uses of Ranges, including Ranges that are captured by Range Adapters. This worked fine in C++98, where users couldn't name the types of the Range Adapters in order to declare local variables, but with C++11's auto keyword, users can now save adapted ranges. Since Boost's adapted ranges capture references to their arguments, which can be temporaries, this is no longer a safe design." Is this claim correct? If so, what's the real-world impact? --Beman

On 4/22/2012 6:21 AM, Beman Dawes wrote:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3350.html claims:
"Boost's range library focuses on defining a set of Range concepts that allow Containers to be Ranges. Because Containers are heavy-weight, this forces Boost to use references for all of their uses of Ranges, including Ranges that are captured by Range Adapters. This worked fine in C++98, where users couldn't name the types of the Range Adapters in order to declare local variables, but with C++11's auto keyword, users can now save adapted ranges. Since Boost's adapted ranges capture references to their arguments, which can be temporaries, this is no longer a safe design."
Is this claim correct?
I imagine so. Expression template libraries (including std::valarray!) have the same problem.
If so, what's the real-world impact?
On the user? "Don't do that." On the library design? Boost.Range *could* detect when an rvalue range is being adapted and steal its guts instead of holding it by reference. This could get expensive if move causes a copy for some heavy range type. My gut is telling me not to do that, though. It's a range *adaptor*. The range being adapted must exist at least as long as the adaptor. A better design (IMHO) would be to detect the internal chained adaptors (what gets returned from transform or filter), and steal *their* guts ... but never try to move the underlying range being adapted. Then the question is only about corner cases like if the predicate of a filter adaptor is heavy and doesn't have a nothrow move c'tor, but I tend to doubt that comes up often. My $.02, -- Eric Niebler BoostPro Computing http://www.boostpro.com

on Sun Apr 22 2012, Eric Niebler <eric-AT-boostpro.com> wrote:
On 4/22/2012 6:21 AM, Beman Dawes wrote:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3350.html claims:
"Boost's range library focuses on defining a set of Range concepts that allow Containers to be Ranges. Because Containers are heavy-weight, this forces Boost to use references for all of their uses of Ranges, including Ranges that are captured by Range Adapters. This worked fine in C++98, where users couldn't name the types of the Range Adapters in order to declare local variables, but with C++11's auto keyword, users can now save adapted ranges. Since Boost's adapted ranges capture references to their arguments, which can be temporaries, this is no longer a safe design."
Is this claim correct?
I imagine so. Expression template libraries (including std::valarray!) have the same problem.
Another way of saying it is, "no more correct than the same complaint about any other type that captures a reference to its ctor argument (e.g. std::ref)." -- Dave Abrahams BoostPro Computing http://www.boostpro.com

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3350.html claims:
"Boost's range library focuses on defining a set of Range concepts that allow Containers to be Ranges. Because Containers are heavy-weight, this forces Boost to use references for all of their uses of Ranges, including Ranges that are captured by Range Adapters. This worked fine in C++98, where users couldn't name the types of the Range Adapters in order to declare local variables, but with C++11's auto keyword, users can now save adapted ranges. Since Boost's adapted ranges capture references to their arguments, which can be temporaries, this is no longer a safe design."
Is this claim correct? If so, what's the real-world impact?
I have run into issues related to this a few times. To give an example, the following works: for (T e : function_returning_vector()) { ... } because the temporary vector is bound to a const reference, so its lifetime is extended to the end of the loop. However, the following does not: for (T e : function_returning_vector() | filtered(pred)) { ... } because while the filtered range's lifetime is extended to the end of the loop, the underlying vector dies before the first iteration. Interestingly (and somewhat inconsistently), if you do it with for_each it works: for_each(function_returning_vector() | filtered(pref), [&]() { ... }; because the temporary vector lives until the end of the statement, and the statement in this case is the whole loop. Regards, Nate
participants (4)
-
Beman Dawes
-
Dave Abrahams
-
Eric Niebler
-
Nathan Ridge