#cpp #cpp11
Есть функция, принимающая rvalue ссылку и передающая её другой функции, принимающей rvalue ссылку: void inner(int&& a) {/* ... */} void outer(int&& a) { inner(std::move(a)); } Я написал std::move, потому что для передачи rvalue ссылок мы вроде как используем std::move. Но есть подозрение, что в данном случае будет вызвана лишняя операция перемещения и для её исключения, возможно, стоит вызвать std::forward, чтобы передать ссылку напрямую, а не создавать временные объекты и перемещать содержимое между ними: void outer(int&& a) { inner(std::forward(a)); } Подскажите, пожалуйста, какой вариант правильней?
Ответы
Ответ 1
Вообще-то, ни в первом, ни во втором случае сами move и forward ни к каким действиям не приводят, и никаких лишних перемещений не вызывают. Как по мне, правильнее и логичнее применение move - потому что у вас четко передается rvalue, а не универсальная ссылка, которая может быть и l, и r (в случае шаблонной функции) - и когда нужно просто передать то, что было получено, т.е. lvalue оставить lvalue, а rvalue - rvalue. Это не ваш случай. По-моему, так. (с) ПухОтвет 2
std::forward требует явного указания шаблонного аргумента и имеет смысл использовать только в контексте шаблонов в универсальными ссылками, которые могут разворачиваться как в rvalue, так и в lvalue. То, что вы написали, а именно std::forward(a) просто напросто не скомпилируется. Т.к. ваши функции принимают rvalue ссылки, то std::move тут единственный правильный выбор. С тем же эффектом можно было бы написать inner(std::forward(a)), но это длиннее.Ответ 3
Ни std::move, ни std::forward никогда ничего не перемещают. Они только изменяют квалификаторы ссылочности. Для int это вообще не актуально, но если у вас есть класс c конструктором перемещения и копирования, эти квалификаторы ссылочности позволяют выбрать нужный конструктор. Изменение квалификаторов происходит так: #include#include struct Foo{ int v = 0; Foo() = default; Foo(int v_): v{v_} {} Foo(const Foo&) = default; Foo(Foo&&) = default; }; void test(){ Foo f1; Foo& ref_foo = f1; const Foo& const_ref_foo = ref_foo; static_assert(std::is_same< Foo&& , decltype(std::move(ref_foo))>{}, ""); static_assert(std::is_same< const Foo&&, decltype(std::move(const_ref_foo))>{}, ""); static_assert( std::is_convertible< const Foo&&, const Foo&>{}, ""); // const Foo&& может вызвать конструктор копирования static_assert( ! std::is_convertible< const Foo&&, Foo&&>{}, ""); // const Foo&& не может вызвать конструктор перемещения static_assert(std::is_same< Foo&& , decltype(std::forward (f1))>{}, ""); // Вызовет конструктор перемещения static_assert(std::is_same< Foo& , decltype(std::forward (ref_foo))>{}, ""); // Вызовет конструктор копирования static_assert(std::is_same< const Foo&&, decltype(std::forward (const_ref_foo))>{}, ""); // Вызовет конструктор копирования } Т.е. в случае void inner(Foo&& a){ } void outer(Foo&& a){ inner(std::move(a)); } При вызове inner ни копирования, ни перемещения не произойдет, будет просто передана ссылка. А вот при таком использовании: void inner(Foo&& a){ static Foo v = std::move(a); } void outer(Foo&& a){ inner(std::move(a)); } При вызове outer произойдет ровно одно перемещение, причем в теле inner, а не outer.
Комментариев нет:
Отправить комментарий