¿Debo seguir devolviendo objetos const en C++ 11?
performance c++11 (3)
La razón por la que su llamada a const_is_returned
dispara el copy constructor
lugar de move constructor
es el hecho de que el move
debe modificar el objeto, por lo tanto, no se puede usar en objetos const
. Tiendo a decir que el uso de const
no es aconsejable en ningún caso y debe ser sometido a un juicio de programador, de lo contrario obtendrá las cosas que ha demostrado. Buena pregunta.
Posible duplicado:
¿Debo devolver objetos const?
(El título original de esa pregunta fue: int foo () o const int foo ()? Explicando por qué me lo perdí.)
C ++ efectivo, elemento 3: use const siempre que sea posible. En particular, se promueve el retorno de objetos const para evitar la asignación involuntaria como if (a*b = c) {
. Me parece un poco paranoico, sin embargo he seguido este consejo.
Me parece que devolver objetos const puede degradar el rendimiento en C ++ 11.
#include <iostream>
using namespace std;
class C {
public:
C() : v(nullptr) { }
C& operator=(const C& other) {
cout << "copy" << endl;
// copy contents of v[]
return *this;
}
C& operator=(C&& other) {
cout << "move" << endl;
v = other.v, other.v = nullptr;
return *this;
}
private:
int* v;
};
const C const_is_returned() { return C(); }
C nonconst_is_returned() { return C(); }
int main(int argc, char* argv[]) {
C c;
c = const_is_returned();
c = nonconst_is_returned();
return 0;
}
Esto imprime:
copy
move
¿Implemento la asignación de movimiento correctamente? ¿O simplemente no debería devolver los objetos const en C ++ 11?
const
decirse que const
un objeto const
por valor nunca fue una muy buena idea, incluso antes de C ++ 11. El único efecto que tiene es que evita que la persona que llama invoque funciones no const en el objeto devuelto, pero eso no es muy relevante dado que la persona que llama recibió una copia del objeto de todos modos.
Si bien es cierto que al ser devuelto, un objeto constante impide que la persona que llama lo use incorrectamente (por ejemplo, haciendo una asignación en lugar de una comparación), no debería ser responsabilidad de una función decidir cómo el llamante puede usar el objeto devuelto ( a menos que el objeto devuelto sea una referencia o un puntero a las estructuras que posee la función). El implementador de funciones no puede saber si el objeto devuelto se usará en una comparación o para otra cosa.
También tiene razón en que en C ++ 11 el problema es aún más grave, ya que devolver un const
efectivamente evita las operaciones de movimiento. (Sin embargo, no impide la elisión copiar / mover).
Por supuesto, es igualmente importante señalar que const
sigue siendo extremadamente útil (y que no utiliza signos de paranoia) cuando la función devuelve una referencia o un puntero.
La devolución de objetos const es una solución alternativa que causa muchos otros problemas. C ++ 11 proporciona una mejor solución para el problema de asignación : calificadores de referencia para funciones miembro. Intento explicarlo con un código:
int foo(); // function declaration
foo() = 42; // ERROR
La expresión de asignación no es válida, y como puede ver, no hay objetos const implicados. Todavía nada especial está sucediendo aquí. Piense en el operador de asignación incorporado de la siguiente manera. Está claro, que la expresión de asignación anterior no es válida mientras que la asignación con una variable nombrada funciona.
int& builtin_assign_int(int& lhs, const int& rhs);
Por lo tanto, es posible restringir los parámetros a lvalue referencias. Sin embargo, no es posible restringir el primer parámetro implícito de las funciones miembro ( *this
) a lvalue references.
Eso cambió con C ++ 11: similar a los calificadores const para funciones miembro, ahora hay calificadores de referencia para funciones miembro. El siguiente código muestra el uso en los operadores de copia y movimiento (tenga en cuenta el &
después de la lista de parámetros):
struct Int
{
Int(const Int& rhs) = default;
Int(Int&& rhs) noexcept = default;
~Int() noexcept = default;
auto operator=(const Int& rhs) & -> Int& = default;
auto operator=(Int&& rhs) & noexcept -> Int& = default;
};
Con esta declaración de clase, la expresión de asignación en el siguiente fragmento de código no es válida, mientras que la asignación a una variable local funciona, como ocurría en el primer ejemplo.
Int bar();
Int baz();
bar() = baz(); // ERROR: no viable overloaded ''=''
Entonces no hay necesidad de devolver objetos const. Puede restringir los operadores de asignaciones a lvalue referencias, para que todo lo demás funcione de la manera esperada, en particular operaciones de movimiento.
Ver también: