
Nathan Ridge wrote:
Here is an example of the manual approach:
template <typename Range> void f(Range&&);
// No lifetime problem. f(std::string("Hello world!") | reversed);
// `moved` is not necessary. f(std::string("Hello world!") | moved | reversed);
In the automatic approach, `std::string("Hello world!") | reversed` returns `moved_range<std::string, boost::fusion::vector<reverse_forwarder> >`.
As opposed to what? You haven't shown me what it returns in the manual approach.
In the manual approach it would return the same thing it does now: reversed_range<std::string>.
Basically, the "automatic approach" is having every adaptor automatically wrap the range in a moved_range *just in case* it's used in a context where it needs to be moved, and the "manual approach" is having a "moved" adaptor that does the wrapping and needs to be used explicitly in such contexts, while leaving other adaptors unchanged.
The tradeoff is between moving the container in contexts where it doesn't have to be moved (where the temporary range's lifetime is long enough) vs. having to remember to add " | moved" in contexts where it does have to be moved.
Exactly right. Thanks for the explanation, Nate! And here is a third option: "automatic approach with opt-out method". This is just an "automatic approach", but an opt-out method is also provided. template <typename Range> inline Range const& dont_move(Range&& rng) { return rng; } With this function, we can avoid the use of moved_range. f(std::string("Hello world!") | reversed); // Use moved_range f(dont_move(std::string("Hello world!")) | reversed); // Don't use moved_range Regards, Michel