Forwarding the same value to two or more functions

There's actually no rvalue-reference version of std::begin - we just have (setting aside constexpr and return values):

template <class C>
??? begin(C& );

template <class C>
??? begin(C const& );

For lvalue containers, you get iterator, and for rvalue containers, you get const_iterator (or whatever the container-specific equivalent ends up being).

The one real problem in your code is returning decltype(auto). For lvalue containers, that's fine - you'll return a reference to an object whose lifetime exceeds the function. But for rvalue containers, that's returning a dangling reference. You'll want to return a reference for lvalue containers and a value for rvalue containers.

On top of that, forward-ing the containers into begin()/end() is probably not what you want to do. It'd be more efficient to conditionally wrap the result of the select() as a move iterator. Something like this answer of mine:

template <typename Container,
          typename V = decltype(*std::begin(std::declval<Container&>())),
          typename R = std::conditional_t<
              std::is_lvalue_reference<Container>::value,
              V,
              std::remove_reference_t<V>
              >
          >
constexpr R operator()(Container&& c)
{
    auto it = select(std::begin(c), std::end(c));
    return *make_forward_iterator<Container>(it);
}

There's probably a less verbose way to express all of that.


In general, it is not reasonable for the same function to forward the same parameter twice. Not unless it has specific knowledge of what the receiver of that forwarded parameter will do.

Remember: the behavior of std::forward can be equivalent to the behavior of std::move, depending on what parameter the user passed in. And the behavior of an xvalue will be contingent on how the receiving function processes it. If the receiver takes a non-const rvalue reference, it will likely move from that value if possible. That would leave you holding a moved-from object. If it takes a value, it will certainly move from it if the type supports it.

So unless you have specific knowledge of the expected behavior of the operations you are using, it is not safe to forward a parameter more than once.