Яка різниця між std :: move та std :: вперед


160

Я побачив це тут: Move Constructor виклику базового класу Move Constructor

Може хтось пояснить:

  1. різниця між std::moveі std::forward, бажано, в деяких прикладах коду?
  2. Як легко про це думати, і коли використовувати

1
Дивіться також ці два пов’язані питання: Чи слід використовувати std :: move або std :: вперед в ctors / операторах призначення? і як працює std :: forward? (так само, як і повторне запитання).
Xeo

"Як легко про це думати, і коли використовувати яке" Ви використовуєте, moveколи хочете перемістити значення, і forwardколи ви хочете використовувати ідеальне переадресація. Це не ракетна наука;)
Нікол Болас

move () виконувати безумовний амплуа, де asward () виконувати передачу на основі параметру, який пройшов.
RaGa__M

Відповіді:


159

std::moveбере об’єкт і дозволяє ставитися до нього як до тимчасового (оцінку). Хоча це не семантична вимога, як правило, функція, що приймає посилання на rvalue, визнає її недійсною. Коли ви бачите std::move, це вказує на те, що значення об'єкта не слід використовувати після цього, але ви все одно можете призначити нове значення та продовжити його використання.

std::forwardмає єдиний випадок використання: для приведення шаблону функціонального параметра (всередині функції) до категорії значень (lvalue або rvalue) абонент, який використовується для його передачі. Це дозволяє передавати аргументи rvalue як rvalues, а lvalues ​​передаватись як lvalues, схема під назвою "ідеальне переадресація".

Для ілюстрації :

void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }

template< typename t >
/* "t &&" with "t" being template param is special, and  adjusts "t" to be
   (for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
    std::cout << "via std::forward: ";
    overloaded( std::forward< t >( arg ) );
    std::cout << "via std::move: ";
    overloaded( std::move( arg ) ); // conceptually this would invalidate arg
    std::cout << "by simple passing: ";
    overloaded( arg );
}

int main() {
    std::cout << "initial caller passes rvalue:\n";
    forwarding( 5 );
    std::cout << "initial caller passes lvalue:\n";
    int x = 5;
    forwarding( x );
}

Як згадує Говард, також існують подібності, оскільки обидві ці функції просто спрямовані на тип посилання. Але поза цими конкретними випадками використання (які охоплюють 99,9% корисності рейтингових рейтингів rvalue), вам слід використовувати static_castбезпосередньо та написати гарне пояснення того, що ви робите.


Я не впевнений , що це точно , що std::forward«S тільки випадок використання є досконалим експедиторська аргументів функції. Я зіткнувся з ситуаціями, коли хочу ідеально передати інші речі, наприклад, учасників об'єкта.
Джефф Ромер

@GeoffRomer Члени параметра? У вас є приклад?
Potatoswatter

Я розмістив приклад як окреме запитання: stackoverflow.com/questions/20616958/…
Джефф Ромер

Хм, так що якщо я правильно це розумію, це означає, що я можу записати одну функцію вперед (), коли вперед (5) працює на 5 так, як ніби я передав її за значенням, коли вперед (x) працює на x, як ніби я передав його посилання без необхідності явно писати перевантаження.
iheanyi

@iheanyi Так, це ідея! Але це повинен бути шаблон, а не просто звичайна функція.
Potatoswatter

63

І те, std::forwardі std::moveінше - лише касти.

X x;
std::move(x);

Вище наведено вираз lvalue xтипу X на вираз rvalue типу X (точне значення x). moveВи також можете прийняти оцінку:

std::move(make_X());

і в цьому випадку це функція ідентичності: приймає ревальвінг типу X і повертає ревальвінг типу X.

З std::forwardви можете вибрати пункт призначення в деякій мірі:

X x;
std::forward<Y>(x);

Віддає вираз значення l xтипу X на вираз типу Y. Існує обмеження щодо того, яким може бути Y.

Y може бути доступною базою X, або посиланням на базу X. Y може бути X, або посиланням на X. Не можна відкидати cv-кваліфікаторів forward, але можна додати cv-кваліфікатори. Y не може бути типом, який може бути конвертованим лише з X, за винятком доступного перетворення Base.

Якщо Y - посилання на значення, результатом буде вираз lvalue. Якщо Y не є посиланням на значення, результатом буде вираз rvalue (xvalue, якщо бути точним).

forwardможе приймати аргумент rvalue лише в тому випадку, якщо Y не є посиланням на значення. Тобто, ви не можете кидати ревальвінг на значення. Це з міркувань безпеки, оскільки це зазвичай призводить до звисання посилань. Але введення рейтингу в rvalue нормально і дозволено.

Якщо ви спробуєте вказати Y на щось, що не дозволено, помилка буде схоплена під час компіляції, а не час виконання.


Якщо я прекрасно пересилаю об’єкт функції, використовуючи std::forward, то після виконання цієї функції я можу використовувати цей об'єкт? Я усвідомлюю, що у випадку std::moveце невизначена поведінка.
iammilind

1
Щодо move: stackoverflow.com/a/7028318/576911 Оскільки forward, якщо ви передаєте значення lvalue, ваш API повинен реагувати так, ніби він отримує значення. Зазвичай це означає, що значення буде незміненим. Але якщо це нецікаве значення, ваш API може змінити його. Якщо ви передаєте в RValue, це зазвичай означає , що ваш API може бути переміщений з нього, і , таким чином , stackoverflow.com/a/7028318/576911 буде застосовуватися.
Говард Хінант

21

std::forwardвикористовується для пересилання параметра точно так, як він був переданий функції. Так само, як показано тут:

Коли використовувати std :: вперед для пересилання аргументів?

Використання std::moveпропозицій об'єкта як оцінювання, можливо, щоб відповідати конструктору переміщення або функції, що приймає rvalues. Це робиться для того, що std::move(x)навіть якщо xвоно не є ревальвальним саме по собі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.