Пересылка отдельных членов ссылки Forward

У меня есть функция, которая потенциально перемещает общий аргумент, но через его членов. Какой из этих вариантов более правильный:

  1. Это кажется более естественным, но странным, потому что аргумент потенциально перемещается дважды [a], что странно, поскольку объект может стать недействительным.

    template<class T> 
    void fun(T&& t){
        myhead_ = std::forward<T>(t).head_;
        myrest_ = std::forward<T>(t).rest_;
    }
    
  2. Это не может быть неправильным, но может ничего не перемещать.

    template<class T> void fun(T&& t){
        myhead_ = std::forward<decltype(t.head_)>(t.head_);
        myrest_ = std::forward<decltype(t.rest_)>(t.rest_);
    }
    
  3. Это кажется правильным, но слишком много кода.

    template<class T> void fun(T& t){
        myhead_ = t.head_;
        myrest_ = t.rest_;
    }
    template<class T> void fun(T&& t){
        myhead_ = std::move(t.head_);
        myrest_ = std::move(t.rest_);
    }
    

[a] Это утверждение неверно, как указал @Angew, оно только выглядит так, как будто оно было перемещено дважды. std::forward (как и std::move) на самом деле ничего не двигает. В лучшем случае элемент перемещается (последующей операцией decltype(myhead)::operator=, но это как раз и является целью).


person alfC    schedule 03.05.2018    source источник


Ответы (1)


Ваш первый код в порядке:

template<class T> 
void fun(T&& t){
    myhead_ = std::forward<T>(t).head_;
    myrest_ = std::forward<T>(t).rest_;
}

Это связано с тем, что стандарт гарантирует, что при выполнении a.b и a является значением x (например, перенаправленной ссылкой на значение r), результат a.b также является значением exvalue (т. е. может быть перемещен). Также обратите внимание, что std::forward и std::move сами по себе не перемещают, они просто приводятся. Таким образом, нет риска дважды перейти от t в вашем коде.

person Angew is no longer proud of SO    schedule 03.05.2018
comment
@alfC Второе решение никогда бы не сдвинулось, поскольку t.head_ является lvalue (поскольку t является lvalue). Вам бы понадобился std::forward внутри decltype. Однако с тех пор я перепроверил стандарт и обнаружил, что во всем этом нет необходимости. - person Angew is no longer proud of SO; 03.05.2018
comment
Круто, вас может заинтересовать этот связанный случай. stackoverflow.com/a/48916134/225186 , которые включают функции-члены вместо членов по идее. - person alfC; 03.05.2018