ejemplos descargar definicion compiler caracteristicas c++

c++ - descargar - Usar std:: move() cuando se devuelve un valor desde una función para evitar copiarlo



c++ manual (3)

No. La mejor práctica es return t; directamente return t; .

En el caso de que la clase T tenga el constructor de movimiento no eliminado, y el aviso t es una variable local que return t es elegible para la elección de copia, mover construye el objeto devuelto al igual que return std::move(t); hace. Sin embargo, return t; aún es elegible para copiar / mover elision, por lo que la construcción se puede omitir, mientras que return std::move(t) siempre construye el valor de retorno utilizando el constructor move.

En caso de que se elimine el constructor de movimiento en la clase T pero el constructor de copia esté disponible, return std::move(t); no compilará, mientras que return t; Todavía compila utilizando copia constructor. A diferencia de mencionado, t no está vinculado a una referencia rvalue. Hay una resolución de sobrecarga de dos etapas para los valores de retorno que son elegibles para la elección de copia: intente mover primero, luego copiar, y posiblemente tanto el movimiento como la copia se eliminen.

class T { public: T () = default; T (T&& t) = delete; T (const T& t) = default; }; T foo() { T t; return t; // OK: copied, possibly elided return std::move(t); // error: move constructor deleted return static_cast<T&>(t); // OK: copied, never elided }

Si la expresión de return es lvalue y no es elegible para la elección de copia (lo más probable es que esté devolviendo una variable no local o una expresión de lvalue) y aún desea evitar la copia, std::move será útil. Pero tenga en cuenta que la mejor práctica es hacer posible la copia.

class T { public: T () = default; T (T&& t) = default; T (const T& t) = default; }; T bar(bool k) { T a, b; return k ? a : b; // lvalue expression, copied return std::move(k ? a : b); // moved if (k) return a; // moved, and possibly elided else return b; // moved, and possibly elided }

12.8 (32) en la norma describe el proceso.

12.8 [class.copy]

32 Cuando los criterios para la elección de una operación de copia se cumplen o se cumplirían, salvo por el hecho de que el objeto de origen es un parámetro de función, y el objeto a copiar se designa mediante un lvalor, resolución de sobrecarga para seleccionar el constructor para la copia. primero se realiza como si el objeto fuera designado por un valor de r. Si la resolución de sobrecarga falla, o si el tipo del primer parámetro del constructor seleccionado no es una referencia de valor al tipo del objeto (posiblemente cv-calificado), la resolución de sobrecarga se realiza nuevamente, considerando el objeto como un valor de l. [Nota: esta resolución de sobrecarga de dos etapas se debe realizar independientemente de si se producirá elision de copia. Determina el constructor que se llamará si no se realiza el elision, y el constructor seleccionado debe ser accesible incluso si la llamada es eluida. "Nota final"

Considere un tipo T que admita la semántica de movimiento predeterminada. Considera también la siguiente función:

T f() { T t; return t; } T o = f();

En el antiguo C ++ 03, algunos compiladores no óptimos pueden llamar al constructor de copia dos veces, uno para el "objeto de retorno" y otro para o .

En c ++ 11, dado que t inside f() es un lvalue, esos compiladores podrían llamar al constructor de copia una vez como antes, y luego llamar al constructor move para o.

¿Es correcto afirmar que la única forma de evitar la primera "copia extra" es mover t al regresar?

T f() { T t; return std::move(t); }


No. Siempre que una variable local en una declaración de return es elegible para la elección de copia, se enlaza a una referencia de rvalor y, por lo tanto, return t; es idéntico a return std::move(t); en su ejemplo con respecto a qué constructores son elegibles.

Tenga en cuenta sin embargo que return std::move(t); evita que el compilador ejerza elision de copia, mientras return t ; No lo hace, y por lo tanto este último es el estilo preferido. [Gracias a @Johannes por la corrección.] Si ocurre una copia de la copia, la cuestión de si se usa o no la construcción de movimientos se convierte en un punto discutible.

Ver 12.8 (31, 32) en la norma.

Tenga en cuenta también que si T tiene una copia accesible, pero un constructor de movimientos eliminado, return t; no compilará, porque el constructor de movimientos debe considerarse primero; tendrías que decir algo al efecto de return static_cast<T&>(t); para que funcione:

T f() { T t; return t; // most likely elided entirely return std::move(t); // uses T::T(T &&) if defined; error if deleted or inaccessible return static_cast<T&>(t) // uses T::T(T const &) }


Ok, me gustaría dejar un comentario sobre esto. Esta pregunta (y la respuesta) me hicieron creer que no es necesario especificar std::move en la declaración de devolución. Sin embargo, solo pensé en una lección diferente al tratar con mi código.

Entonces, tengo una función (en realidad es una especialización) que toma una temporal y simplemente la devuelve. (La plantilla de función general hace otras cosas, pero la especialización hace la operación de identidad).

template<> struct CreateLeaf< A > { typedef A Leaf_t; inline static Leaf_t make( A &&a) { return a; } };

Ahora, esta versión llama al constructor de copia de A al regresar. Si cambio la declaración de retorno a

Leaf_t make( A &&a) { return std::move(a); }

Luego se llama al constructor de movimientos de A y puedo hacer algunas optimizaciones allí.

Puede que no coincida al 100% con tu pregunta. Pero es falso pensar que return std::move(..) nunca es necesario. Solía ​​pensar así. Ya no ;-)