Идеальный элемент контейнера для пересылки

Аналогично этому вопросу, но вместо идеального пересылающего члена объекта я хотел бы знать, как усовершенствовать пересылку элементов контейнера STL, т.е. аналогично

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]));
  }
}

person Kan Li    schedule 21.04.2015    source источник
comment
Можете ли вы привести пример того, что вы пытаетесь сделать?   -  person Barry    schedule 21.04.2015
comment
@Barry, я следовал правилу sscce.org, чтобы сократить его до минимума. Фактический вариант использования сложен. Я хотел бы создать vector<T> из vector<U1>, vector<U2>, ...., где каждый элемент T построен из U1, U2, .... Каждый массив vector<Ui> может быть либо &, либо &&, и я бы хотел, чтобы Ui быть идеально перенаправлены.   -  person Kan Li    schedule 21.04.2015
comment
Что-то вроде coliru.stacked-crooked.com/a/8aadafc70b8c979b?   -  person T.C.    schedule 21.04.2015
comment
@Barry Цель состоит в том, чтобы перейти от v[i], если v было привязано к rvalue.   -  person T.C.    schedule 21.04.2015


Ответы (1)


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]));
}

Демо. Использование std::forward в реализации автоматически предотвращает опасное перенаправление rvalue как lvalue.

Для вашего фактического варианта использования

Я хочу создать vector<T> из vector<U1>, vector<U2>, ...., где каждый элемент T состоит из U1, U2, .... Каждый массив vector<Ui> может быть либо &, либо &&, и я бы хотел, чтобы Ui перенаправлялся без ошибок.

это становится чем-то вроде

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;
}
person T.C.    schedule 21.04.2015
comment
Итак, если у нас есть ссылка на кортеж rvalue из ссылок lvalue, должен ли forward_like<Tuple>( get_tuple_member(t) ) возвращать rvalue или lvalue? Я предполагаю, что мы не можем отличить член от ссылочного члена из функции доступа. Может быть, мы должны делать std::forward<Vector>(v)[i], а vector::operator[] должны иметь перегрузку rvalue? Хурм. Теоретически, std::forward<Foo>(foo).field это то, чему мы стремимся здесь подражать, не так ли? - person Yakk - Adam Nevraumont; 21.04.2015
comment
@Yakk Да, поэтому пересылка элемента ссылки lvalue кортежа rvalue должна создавать lvalue. (Я полагаю, что std::get уже справился с этим?) - person T.C.; 21.04.2015
comment
кивнуть, но описанное выше не может: единственный способ сделать это — находиться внутри класса и знать, владеете ли вы T или T&, так как любой разумный метод доступа вернет T& в обоих случаях. Я просто заметил, что если бы у вас было (несуществующее) vector<int&>, вышеприведенное было бы move-from, если бы контейнер был rvalue, и подумал, есть ли способ обойти это. Я не вижу ни одного. Принципиально делает forward_like<U> слабее, чем методы доступа, поддерживающие rvalue. А методы доступа, поддерживающие rvalue, всегда казались грязными (вам нужно знать, что вы на самом деле не перемещаетесь, а просто перемещаете это одно поле). - person Yakk - Adam Nevraumont; 21.04.2015
comment
@Yakk Действительно, нам нужно знать, действительно ли хранимая вещь является ссылкой, что означает какой-то способ напрямую запросить контейнер (tuple_element_t с кортежами; возможно, value_type для контейнеров). - person T.C.; 21.04.2015