para - libro de c++ completo
¿Tiene D algo similar a la semántica de movimientos de C++ 0x? (5)
Creo que hay varios lugares en D (como devolver estructuras) que D logra hacer que se muevan, mientras que C ++ los haría una copia. IIRC, el compilador hará un movimiento en lugar de una copia en cualquier caso en el que pueda determinar que no es necesaria una copia, por lo que la copia de estructura se realizará menos en D que en C ++. Y, por supuesto, dado que las clases son referencias, no tienen el problema en absoluto.
Pero independientemente, la construcción de copias ya funciona de forma diferente en D que en C ++. Generalmente, en lugar de declarar un constructor de copia, declara un constructor postblit: this(this)
. Hace un memcpy completo antes de que se llame a this(this)
, y solo hace los cambios necesarios para garantizar que la nueva estructura esté separada del original (como hacer una copia profunda de las variables miembro cuando sea necesario), en lugar de crear Un constructor completamente nuevo que debe copiar todo. Por lo tanto, el enfoque general ya es un poco diferente de C ++. Por lo general, también se acepta que las estructuras no deberían tener constructores postblit costosos (copiar estructuras debería ser barato), por lo que es un problema menor que en C ++. Los objetos que serían costosos de copiar son generalmente clases o estructuras con referencia o semántica COW.
Los contenedores son generalmente tipos de referencia (en Phobos, son estructuras en lugar de clases, ya que no necesitan polimorfismo, pero copiarlos no copia su contenido, por lo que aún son tipos de referencia), por lo que copiarlos no es costoso como sería en C ++.
Es muy posible que haya casos en D en los que se pueda usar algo similar a un constructor de movimientos, pero en general, D ha sido diseñado de tal manera que reduce los problemas que tiene C ++ al copiar objetos, por lo que no está ni cerca del problema. que está en C ++.
Un problema de los "tipos de valor" con recursos externos (como std::vector<T>
o std::string
) es que copiarlos tiende a ser bastante costoso, y las copias se crean implícitamente en varios contextos, por lo que este tiende a ser un problema. preocupación de rendimiento La respuesta de C ++ 0x a este problema es la semántica de movimientos , que se basa conceptualmente en la idea del robo de recursos y está técnicamente impulsada por referencias de valores r .
¿Tiene D algo similar para mover semánticas o referencias de valores?
Creo que si necesita la fuente para perder el recurso puede estar en problemas. Sin embargo, al estar en GC, a menudo se puede evitar la necesidad de preocuparse por los múltiples propietarios, por lo que puede que no sea un problema para la mayoría de los casos.
Creo que todas las respuestas no respondieron completamente a la pregunta original.
Primero, como se indicó anteriormente, la pregunta solo es relevante para estructuras. Las clases no tienen movimiento significativo. También se indicó anteriormente, para estructuras, una cierta cantidad de movimiento ocurrirá automáticamente por el compilador bajo ciertas condiciones.
Si desea obtener control sobre las operaciones de movimiento, esto es lo que tiene que hacer. Puede deshabilitar la copia anotando esto (esto) con @disable. A continuación, puede anular el constructor(constructor &&that)
C ++ constructor(constructor &&that)
definiendo this(Struct that)
. Del mismo modo, puede anular la asignación con opAssign(Struct that)
. En ambos casos, debe asegurarse de destruir los valores de that
.
Para la asignación, ya que también es necesario destruir el antiguo valor de this
, la forma más sencilla es intercambiarlos. Una implementación de unique_ptr
de C ++, por lo tanto, sería algo como esto:
struct UniquePtr(T) {
private T* ptr = null;
@disable this(this); // This disables both copy construction and opAssign
// The obvious constructor, destructor and accessor
this(T* ptr) {
if(ptr !is null)
this.ptr = ptr;
}
~this() {
freeMemory(ptr);
}
inout(T)* get() inout {
return ptr;
}
// Move operations
this(UniquePtr!T that) {
this.ptr = that.ptr;
that.ptr = null;
}
ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that"
swap(this.ptr, that.ptr); // We change it anyways, because it''s a temporary
return this;
}
}
Edición: Note que no opAssign(ref UniquePtr!T that)
. Ese es el operador de asignación de copia, y si intenta definirlo, el compilador generará un error porque usted declaró, en la línea @disable
, que no tiene tal cosa.
D tiene un valor separado y una semántica de objeto:
- Si declara su tipo como
struct
, tendrá valor semántico por defecto. - Si declara su tipo como
class
, tendrá objeto semántico.
Ahora, suponiendo que no administre la memoria usted mismo, ya que es el caso predeterminado en D (usar un recolector de basura), debe comprender que el objeto de los tipos declarados como class
son punteros (o "referencia" si lo prefiere) a la Objeto real, no el objeto real en sí.
Entonces, al pasar vectores alrededor de D, lo que pasa es la referencia / puntero. Automáticamente. No hay copia involucrada (aparte de la copia de la referencia).
Es por eso que D, C #, Java y otros lenguajes no "necesitan" movimiento semántico (ya que la mayoría de los tipos son objetos semánticos y se manipulan por referencia, no por copia).
Tal vez podrían implementarlo, no estoy seguro. ¿Pero realmente obtendrían un aumento de rendimiento como en C ++? Por naturaleza, no parece probable.
De alguna manera, tengo la sensación de que, en realidad, las referencias de rvalor y todo el concepto de "mover semántica" es una consecuencia de que es normal en C ++ crear objetos de pila locales "temporales". En D y en la mayoría de los idiomas de GC, es más común tener objetos en el montón, y luego no hay sobrecarga con tener un objeto temporal copiado (o movido) varias veces al devolverlo a través de una pila de llamadas , por lo que no es necesario un mecanismo Evita esa sobrecarga también.
En D (y en la mayoría de los idiomas de GC), un objeto de class
nunca se copia implícitamente y solo está pasando la referencia la mayor parte del tiempo, por lo que esto puede significar que no necesita ninguna referencia de valor para ellos.
OTOH, los objetos de struct
NO se supone que sean "manejadores de recursos", sino tipos de valores simples que se comportan de manera similar a los tipos incorporados, así que, una vez más, no hay razón para ninguna semántica de movimientos aquí, IMHO.
Esto daría una conclusión: D no tiene referencias de valor porque no las necesita .
Sin embargo, no he usado referencias de valores en la práctica, solo he leído sobre ellas, por lo que podría haber omitido algunos casos de uso reales de esta función. Por favor, trate esta publicación como un montón de pensamientos sobre el tema que esperamos le sean útiles, no como un juicio confiable.