Pregunta

Tengo un ++ datos de estructura C que se requiere es un "bloc de notas" para otros cálculos. No es vivió largo, y no se utiliza con frecuencia para no críticos de rendimiento. Sin embargo, incluye un generador de números aleatorios entre otros campos de seguimiento actualizables, y mientras que el valor real del generador no es importante, es importante que el valor se actualiza en lugar de copiar y volver a utilizar. Esto significa que en general, los objetos de esta clase se pasan por referencia.

Si una instancia sólo es necesaria una vez, el enfoque más natural es la de construir ellos whereever necesario (tal vez usando un método de fábrica o un constructor), y luego pasar el scratchpad con el método consume. firmas de los métodos de los consumidores utilizan pasan por referencia, ya que no saben que es el único uso, pero los métodos de fábrica y constructores regresan por valor -. y no se puede pasar temporales identificadas por referencia

¿Hay una manera de evitar que se obstruya el código con variables temporales desagradables? Me gustaría evitar cosas como la siguiente:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);
xyz.initialize_computation(useless_temp);

Yo podría hacer que la memoria auxiliar intrínsecamente mutable y simplemente etiquetar todos los parámetros const &, pero eso no me parece que sea la mejor práctica ya que es engañosa, y no puedo hacer esto para las clases I no controlan totalmente. El paso por referencia rvalue requeriría la adición de sobrecargas a todos los consumidores de bloc de notas, que tipo de derrotas el propósito -. Tener código claro y conciso

Teniendo en cuenta el hecho de que el rendimiento no es crítico (pero el tamaño del código y la legibilidad son), ¿cuál es el planteamiento de las mejores prácticas a pasar en un bloc de notas tal? Uso de C ++ 0x cuenta está bien si < em> requerida pero preferiblemente C ++ 03-únicas características debería ser suficiente.

Editar: Para ser claros, el uso de un temporal es factible, es simplemente lamentable desorden en el código me gustaría evitar. Si nunca da el nombre de un temporal, es evidente que sólo se usa una vez, y el menor número de líneas de código para leer, mejor. Además, en inicializadores de constructores, es imposible declarar temporales.

¿Fue útil?

Solución

A pesar de que no está bien para pasar rvalues ??a las funciones que aceptan referencias no constante, que está bien para llamar a funciones miembro de rvalues, pero la función miembro no sabe cómo se llamaba. Si devuelve una referencia al objeto actual, puede convertir a rvalues ??lvalues:

class scratchpad_t
{
    // ...

public:

    scratchpad_t& self()
    {
        return *this;
    }
};

void foo(scratchpad_t& r)
{
}

int main()
{
    foo(scratchpad_t().self());
}

Nota cómo la llamada a self() se obtiene una expresión lvalue a pesar de que scratchpad_t es un valor p.

Por favor, corríjanme si me equivoco, pero los parámetros de referencia RValue no aceptan referencias lvalue lo que su uso requeriría la adición de sobrecargas a todos los consumidores de bloc de notas, que también es lamentable.

Bueno, usted podría utilizar plantillas ...

template <typename Scratch> void foo(Scratch&& scratchpad)
{
    // ...
}

Si llama foo con un parámetro de valor de lado derecho, Scratch se deduce a scratchpad_t, y por lo tanto será Scratch&& scratchpad_t&&.

Y si se llama foo con un parámetro de valor-I, Scratch se deduce a scratchpad_t&, y debido a las normas de referencia colapso, Scratch&& también será scratchpad_t&.

Tenga en cuenta que la scratchpad parámetro formal es un nombre y por lo tanto un valor-I, no importa si su tipo es una referencia lvalue o una referencia de valor de lado derecho. Si desea pasar scratchpad a otras funciones, no es necesario el truco plantilla para aquellas funciones más, sólo tiene que utilizar un parámetro de referencia de valor-I.

Por cierto, ¿te das cuenta de que la memoria provisional involucrado en xyz.initialize_computation(scratchpad_t(1, 2, 3)); será destruida tan pronto como initialize_computation se hace, ¿verdad? El almacenamiento de la referencia dentro del objeto xyz para el usuario más tarde sería una muy mala idea.

self() no tiene por qué ser miembro de un método, que puede ser una función con plantilla

Sí, eso también es posible, aunque me gustaría cambiar su nombre para hacer más clara la intención:

template <typename T>
T& as_lvalue(T&& x)
{
    return x;
}

Otros consejos

¿El problema es simplemente que esto:

scratchpad_t<typeX<typeY,potentially::messy>, typename T> useless_temp = factory(rng_parm);

es feo? Si es así, entonces ¿por qué no cambiarlo a esto:?

auto useless_temp = factory(rng_parm);

En lo personal, yo preferiría ver const_cast que mutable. Cuando veo mutable, estoy suponiendo que alguien está haciendo const-dad lógica, y no creo que gran parte de ella. Sin embargo const_cast plantea banderas rojas, como un código como éste debería.

Una opción sería utilizar algo así como shared_ptr (auto_ptr funcionaría también en función de lo factory está haciendo) y pasarlo por valor, lo que evita el costo de copia y mantiene una única instancia, sin embargo, se puede pasar desde su fábrica método.

Si asigna el objeto en el montón que podría ser capaz de convertir el código a algo como:

std::auto_ptr<scratch_t> create_scratch();

foo( *create_scratch() );

La fábrica crea y devuelve un auto_ptr en lugar de un objeto en la pila. El auto_ptr regresado temporal tendrá la propiedad del objeto, sino que se les permite llamar a los métodos no const en un temporal y puede eliminar la referencia al puntero para obtener una referencia real. En el siguiente punto de la secuencia de puntero inteligente será destruido y liberado la memoria. Si tiene que pasar el mismo scratch_t a diferentes funciones en una fila que sólo puede capturar el puntero inteligente:

std::auto_ptr<scratch_t> s( create_scratch() );
foo( *s );
bar( *s );

Esto puede ser reemplazado con std::unique_ptr en el próximo estándar.

Me marcó la respuesta de FredOverflow como la respuesta a su sugerencia de utilizar un método que devuelva una referencia no const; Esto funciona de C ++ 03. Esta solución requiere un método miembro por scratchpad-como tipo, pero en C ++ 0x también puede escribir que el método más general para cualquier tipo:

template <typename T> T & temp(T && temporary_value) {return temporary_value;}

Esta función simplemente hacia delante normales referencias lvalue, y convertidos RValue referencias en referencias lvalue. Por supuesto, hacer esto devuelve un valor modificable cuyo resultado se ignora -. Que resulta ser exactamente lo que quiero, pero puede parecer extraño en algunos contextos

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