2

Similar to this question, but instead of perfect forwarding member of an object, I would like to know how to perfect forwarding elements of an STL container, i.e. similar to

struct X {};
void f(X&);
void f(X&&);

template <typename Vector>
void g(Vector&& v, size_t i) {
  if (is_lvalue_reference<Vector>::value) {
    f(v[i]);
  } else {
    f(move(v[i]));
  }
}
Community
  • 1
  • 1
Kan Li
  • 8,557
  • 8
  • 53
  • 93
  • Can you give an example of what you're trying to do? – Barry Apr 21 '15 at 18:07
  • @Barry, I have followed to rule http://sscce.org/ to trim it to a minimum case. The actual use case is complicated. I'd like to create `vector` from `vector`, `vector`, ...., where each element `T` is constructed from `U1`, `U2`, .... Each array of `vector` could be either & or &&, and I'd like the `Ui` to be perfectly forwarded. – Kan Li Apr 21 '15 at 18:08
  • 1
    Something like http://coliru.stacked-crooked.com/a/8aadafc70b8c979b? – T.C. Apr 21 '15 at 18:11
  • @Barry The goal is to move from `v[i]` iff `v` was bound to an rvalue. – T.C. Apr 21 '15 at 18:12

1 Answers1

7
namespace detail {
    template<class T, class U>
    using forwarded_type = std::conditional_t<std::is_lvalue_reference<T>::value,
                                              std::remove_reference_t<U>&, 
                                              std::remove_reference_t<U>&&>;
}
template<class T, class U>
detail::forwarded_type<T,U> forward_like(U&& u) {
    return std::forward<detail::forwarded_type<T,U>>(std::forward<U>(u));
}

template <typename Vector>
void g(Vector&& v, size_t i) {
  f(forward_like<Vector>(v[i]));
}

Demo. Using std::forward in the implementation automatically prevents you from doing a dangerous forward of rvalue as lvalue.

For your actual use case

I'd like to create vector<T> from vector<U1>, vector<U2>, ...., where each element T is constructed from U1, U2, .... Each array of vector<Ui> could be either & or &&, and I'd like the Ui to be perfectly forwarded.

this becomes something like

template<class T, class...Vectors>
std::vector<T> make_vector(Vectors&&...vectors){
    auto n = std::min({vectors.size()...});
    std::vector<T> ret; 
    ret.reserve(n);
    for(decltype(n) i = 0; i < n; ++i)
        ret.emplace_back(forward_like<Vectors>(vectors[i])...);
    return ret;
}
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • So, if we have a rvalue tuple reference of lvalue references, should `forward_like( get_tuple_member(t) )` return an rvalue or lvalue? I guess we cannot distinguish a "member" from a "reference member" from the accessor function. Maybe we should be doing `std::forward(v)[i]` and `vector::operator[]` should have an rvalue overload? Hurm. In theory, `std::forward(foo).field` is what we are aiming to emulate here, no? – Yakk - Adam Nevraumont Apr 21 '15 at 20:03
  • @Yakk Yes, so forwarding an lvalue reference member of an rvalue tuple should produce an lvalue. (I believe `std::get` already handles it?) – T.C. Apr 21 '15 at 20:06
  • *nod*, but the above cannot: the only way to do that is to be "inside" the class and know if you own a `T` or a `T&`, as any sensible accessor will return `T&` in both cases. I was just noting that if you had a (non-existent) `vector`, the above would `move`-from if the container was an rvalue, and thinking if there was a way around it. I don't see one. Makes `forward_like` weaker than rvalue-aware accessors, fundamentally. And rvalue-aware accessors always seemed dirty (you need to know that you aren't *really* moving, just moving that one field). – Yakk - Adam Nevraumont Apr 21 '15 at 20:10
  • @Yakk Indeed, we'll need to know whether the thing stored is actually a reference, which means some way to query the container directly (`tuple_element_t` with tuples; perhaps `value_type` for containers). – T.C. Apr 21 '15 at 20:28