auto z = reverse(vector<int>{1, 2, 3});
yes, using z.iterable is undefined behavior due to a reference to zombies (there is no temporary extension of lifespan because there is no vector <> temporary or a link to a binding)
vector<int> c{ 1, 2, 3 }; auto w = reverse(move(c));
this is normal, move (c) just translates c into vector<int>&& , w.iterable referers to c, but notice that nothing is moved.
What would be a suitable way to build a wrapper for a structure that takes r and l-values, if possible, while maintaining the constant of the streamlined object?
regarding the lifetime of an object, given the “clean” shell (ie, something containing links), you cannot. You should always ensure that no dangling links appear. Of course, you can always allow copy / move-construct rvalues to your instances, but this is rarely useful, I would say.
if the question is how do you pass an argument that preserves its non / const l / rvaluesness, it is called perfect forwarding. But this is not what you want here, because it makes no sense for your shell to store the rvalue link.
so something like
template <typename T> reversion_wrapper<std::remove_reference_t<T>> reverse( T&& iterable ) { return { iterable }; }
would do (the strict remove_reference <> condition is not required here, but this makes a more reasonable choice for a shell parameter). Moreover, it is your choice if you want to disable rvalues at all (for example, if you expect your wrapper to never be used with temporary ones). In this case, you will either be static_assert () inside reverse () or = delete for (const T & &), or use SFINAE to filter the overload.
I can bind an r-value variable to const & so I'm wondering if there would be something like this?
easier / cleaner to overload T & and T const & in this case:
template <typename T> reversion_wrapper<T> reverse( T& iterable ) { return { iterable }; } template <typename T> reversion_wrapper<const T> reverse( T const& iterable ) { return { iterable }; }
but I don’t understand why you need it.