c++ c++11 copy-constructor

c++ - ¿Podemos devolver los objetos que tienen un constructor eliminado/copia privada/mover por valor de una función?



c++11 copy-constructor (5)

Me preguntaba, ¿se levantó esta restricción en C ++ 11?

¿Como puede ser? Al devolver algo por valor, por definición lo estás copiando (o moviendo). Y mientras que C ++ puede permitir que esa copia / movimiento se elimine en ciertas circunstancias, todavía se está copiando (o moviendo) según la especificación.

Recuerdo que podría ser útil permitir que las personas que llaman a una función utilicen el objeto devuelto, pero no pueden copiar el valor y almacenarlo en algún lugar.

Sí. Usted se deshace de la copia del constructor / asignación, pero permite que el valor se mueva . std::unique_ptr hace esto.

Puedes devolver un unique_ptr por valor. Pero al hacerlo, estás devolviendo un "prvalue": un temporal que se está destruyendo. Por lo tanto, si tienes una función g como tal:

std::unique_ptr<SomeType> g() {...}

Puedes hacerlo:

std::unique_ptr<SomeType> value = g();

Pero no esto :

std::unique_ptr<SomeType> value1 = g(); std::unique_ptr<SomeType> value2 = g(); value1 = value 2;

Pero esto es posible:

std::unique_ptr<SomeType> value = g(); value = g();

La segunda línea invoca el operador de asignación de movimiento en value . Se eliminará el puntero antiguo y se moverá el nuevo puntero hacia él, dejando el valor antiguo vacío.

De esta manera, puede asegurarse de que el contenido de cualquier unique_ptr solo se almacene en un solo lugar. No puede evitar que unique_ptr referencia a él en varios lugares (mediante los punteros a unique_ptr o lo que sea), pero habrá como máximo una ubicación en la memoria donde se almacena el puntero real.

La eliminación de los constructores de copiar y mover crea un objeto inmóvil . Donde se crea es donde se guardan los valores, para siempre . El movimiento te permite tener una propiedad única, pero sin estar inmóvil.

En C ++ 03 es imposible devolver un objeto de una clase que tenga un constructor de copia no definido privado por valor:

struct A { A(int x) { ... } private: A(A const&); }; A f() { return A(10); // error! return 10; // error too! }

Me preguntaba, ¿se eliminó esta restricción en C ++ 11, lo que hace posible escribir funciones que tienen un tipo de clase de retorno para clases sin constructores utilizados para copiar o mover? Recuerdo que podría ser útil permitir que las personas que llaman a una función utilicen el objeto recién devuelto, pero que no puedan copiar el valor y almacenarlo en algún lugar.


Así es como puede funcionar.

A f() { return { 10 }; }

Esto funciona aunque A no tenga una copia de trabajo o un constructor de movimiento y ningún otro constructor que pueda copiar o mover un A !

Para hacer uso de esta característica de C ++ 11, el constructor (tomando int en este caso) tiene que ser no explícito.


El código anterior aún está mal formado en C ++ 11. Pero podría agregar un constructor de movimiento público a A y luego sería legal:

struct A { A(int x) {} A(A&&); private: A(A const&); }; A f() { return A(10); // Ok! }


La restricción no ha sido levantada. Según el especificador de acceso, hay una nota en §12.8 / 32 que explica:

la resolución de sobrecarga de dos etapas se debe realizar independientemente de si se producirá o no la 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.

A partir de los constructores de copia / movimiento eliminados, §8.4.3 / 2 establece que

Un programa que hace referencia a una función eliminada implícita o explícitamente, aparte de declararla, está mal formado. [Nota: Esto incluye llamar a la función implícita o explícitamente y formar un puntero o puntero a miembro para la función. Se aplica incluso para referencias en expresiones que no son potencialmente evaluadas. Si una función está sobrecargada, se hace referencia solo si la función se selecciona por resolución de sobrecarga. - nota final]

No estoy seguro de este caso en particular, pero mi comprensión de la cita es que, si después de la resolución de sobrecarga en §12.8 / 32 se selecciona el constructor de copiar / mover eliminado, incluso si la operación es elid, eso podría constituir una referencia a la función , y el programa estaría mal formado.


Probablemente podría juntar un proxy para hacer el truco si realmente quisiera, y tener un constructor de conversión que copie el valor almacenado en el proxy.

Algo a lo largo de las líneas de:

template<typename T> struct ReturnProxy { //This could be made private, provided appropriate frienship is granted ReturnProxy(T* p_) : p(p_) { } ReturnProxy(ReturnProxy&&) = default; private: //don''t want these Proxies sticking around... ReturnProxy(const ReturnProxy&) = delete; void operator =(const ReturnProxy&) = delete; void operator =(ReturnProxy&&) = delete; struct SUPER_FRIENDS { typedef T GO; }; friend struct SUPER_FRIENDS::GO; unique_ptr<T> p; }; struct Object { Object() : data(0) { } //Pseudo-copy constructor Object(ReturnProxy<Object>&& proxy) : data(proxy.p ? proxy.p->data : throw "Don''t get sneaky with me //glare") { //steals `proxy.p` so that there isn''t a second copy of this object floating around //shouldn''t be necessary, but some men just want to watch the world burn. unique_ptr<Object> thief(std::move(proxy.p)); } private: int data; Object(const Object&) = delete; void operator =(const Object&) = delete; }; ReturnProxy<Object> func() { return ReturnProxy(new Object); } int main() { Object o(func()); }

Sin embargo, probablemente podría hacer lo mismo en 03, usando auto_ptr s. Y, obviamente, no impide el almacenamiento del Object resultante, aunque lo limita a una copia por instancia.