Pregunta

Un problema de "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 esto tiende a ser un problema de rendimiento.La respuesta de C++ 0x a este problema es mover semántica, que se basa conceptualmente en la idea del robo de recursos y técnicamente está impulsado por referencias de valores.

¿D tiene algo similar para mover la semántica o las referencias de valores?

¿Fue útil?

Solución

Creo que hay varios lugares en D (como la devolución de estructuras) en los que D logra realizar movimientos, 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 estructuras ocurrirá menos en D que en C++.Y claro, como las clases son referencias, no tienen ningún problema.

Pero independientemente, la construcción de copias ya funciona de manera diferente en D que en C++.Generalmente, en lugar de declarar un constructor de copia, se declara un constructor postblit: this(this).Hace una memoria completa antes this(this) se llama, y ​​solo realiza los cambios necesarios para garantizar que la nueva estructura esté separada de la 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.Entonces, el enfoque general ya es un poco diferente al de C++.En general, también se acepta que las estructuras no deberían tener costosos constructores postblit (copiar estructuras debería ser barato), por lo que es un problema menor de lo que sería en C++.Los objetos que serían costosos de copiar generalmente son clases o estructuras con semántica de referencia o COW.

Los contenedores generalmente son 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 siguen siendo 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 podría 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 se acerca al problema. que está en C++.

Otros consejos

D tienen semántica de valor y objeto independiente:

  • Si usted declara su tipo que struct, tendrá valor semántico de forma predeterminada
  • Si usted declara su tipo que class, tendrá objeto semántico.

Ahora, suponiendo que no maneja la memoria a sí mismo, ya que es el caso por defecto en D - usando un recolector de basura - usted tiene que entender ese objeto de tipos declarado como class son automáticamente punteros (o "de referencia", si lo prefiere ) con el objeto real, no el objeto real mismo.

Así, al pasar alrededor de vectores en D, lo que pasa es la referencia / puntero. Automáticamente. No se copia involucrados (que no sea la copia de la referencia).

Es por eso que D, C #, Java y otro idioma no "necesidad" en movimiento semántica (como la mayoría de los tipos son objeto y semántica son manipulados por referencia, no por copia).

Tal vez se podría ponerlo en práctica, no estoy seguro. Pero ¿realmente conseguir aumento de rendimiento como en C ++? Por naturaleza, que no parece probable.

De alguna manera tengo la sensación de que en realidad las referencias de rvalue y todo el concepto de "semántica de movimiento" es una consecuencia de que es normal en C++ crear objetos de pila locales y "temporales". En D y en la mayoría de los lenguajes GC, es más común tener objetos en el montón, y luego no hay gastos generales al copiar (o mover) un objeto temporal varias veces al devolverlo a través de una pila de llamadas. - por lo que no hay necesidad de un mecanismo para evitar esa sobrecarga también.

En D (y en la mayoría de los lenguajes GC) a class El objeto nunca se copia implícitamente y solo pasas la referencia la mayor parte del tiempo, por lo que esto puede significa que no necesita ninguna referencia de valor para ellos.

OTOH, struct NO se supone que los objetos sean "identificadores de recursos", sino tipos de valores simples que se comportan de manera similar a los tipos integrados; por lo tanto, nuevamente, no hay razón para ninguna semántica de movimiento aquí, en mi humilde opinión.

Esto llevaría a una conclusión: D no tiene referencias de valor porque no las necesita.

Sin embargo, no he usado referencias de rvalue en la práctica, solo las leí, por lo que es posible que me haya saltado algunos casos de uso reales de esta característica.Trate esta publicación como un conjunto de ideas sobre el tema que, con suerte, le serán útiles, no como un juicio confiable.

Creo que todas las respuestas fracasaron por completo para responder a la pregunta original.

En primer lugar, como se ha dicho, la cuestión es sólo relevante para estructuras. Las clases tienen ningún movimiento significativo. También se ha dicho anteriormente, para estructuras, una cierta cantidad de movimiento se producirá automáticamente por el compilador bajo ciertas condiciones.

Si desea obtener el control sobre las operaciones de movimiento, esto es lo que tiene que hacer. Puede desactivar la copia de la anotación de este (este) con @disable. A continuación, se puede anular constructor(constructor &&that) C ++ 's definiendo this(Struct that). Del mismo modo, se puede anular el Asignar con opAssign(Struct that). En ambos casos, es necesario asegurarse de que destruya los valores de that.

Para la asignación, ya que también es necesario para destruir el antiguo valor de this, la forma más sencilla es para intercambiarlas. Una implementación de unique_ptr C ++ 's sería, por lo tanto, ser 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;
    }
}

Editar: Aviso Yo no defino opAssign(ref UniquePtr!T that). Ese es el operador de asignación de copia, y si se intenta definirlo, el compilador error porque usted declaró, en la línea de @disable, que no tiene tal cosa.

Creo que si se necesita la fuente de desatar el recurso que podría estar en problemas. Sin embargo siendo GC'ed a menudo se puede evitar la necesidad de preocuparse por varios propietarios por lo que podría no ser un problema para la mayoría de los casos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top