Я видел это здесь: Move Constructor, вызывающий базовый класс Move Constructor
Может ли кто-нибудь объяснить:
- разница между
std::move
иstd::forward
, желательно с некоторыми примерами кода? - Как легко об этом думать, и когда использовать
Я видел это здесь: Move Constructor, вызывающий базовый класс Move Constructor
Может ли кто-нибудь объяснить:
std::move
и std::forward
, желательно с некоторыми примерами кода? std::move
принимает объект и позволяет рассматривать его как временное (значение rvalue). Хотя это не семантическое требование, обычно функция, принимающая ссылку на 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
и std::move
- не что иное, как отбрасывание.
X x;
std::move(x);
Вышеприведенное выражение lvalue x
типа X относится к выражению rvalue типа X (точное значение x). move
также может принимать значение r:
std::move(make_X());
и в этом случае это тождественная функция: принимает r-значение типа X и возвращает r-значение типа X.
С помощью std::forward
вы можете в какой-то степени выбрать пункт назначения:
X x;
std::forward<Y>(x);
Выдает выражение l x
типа X выражению типа Y. Существуют ограничения на то, что Y может быть.
Y может быть доступной базой X или ссылкой на базу X. Y может быть X или ссылкой на X. Нельзя отбрасывать cv-квалификаторы с forward
, но можно добавить cv -qualifiers. Y не может быть типом, который просто конвертируется из X, за исключением использования доступного преобразования Base.
Если Y является ссылкой на lvalue, результатом будет выражение lvalue. Если Y не является ссылкой lvalue, результатом будет выражение rvalue (значение xvalue to exact).
forward
может принимать аргумент rvalue только в том случае, если Y не является ссылкой lvalue. То есть вы не можете присвоить значение rvalue для lvalue. Это по соображениям безопасности, так как это обычно приводит к оборванным ссылкам. Но приведение rvalue к rvalue в норме и разрешено.
Если вы попытаетесь указать Y на то, что не разрешено, ошибка будет обнаружена во время компиляции, а не время выполнения.
std::forward
используется для пересылки параметра точно так, как он передавался функции. Как показано здесь:
Когда использовать std:: forward для пересылки аргументов?
Использование std::move
предлагает объект как rvalue, возможно, соответствующий конструктору перемещения или функции, принимающей значения r. Он делает это для std::move(x)
, даже если x
не является rvalue сам по себе.