2011-01-31 9 views
7

¿es posible restringir las instancias de clase para usar solo como valores r (por ejemplo, temporales)?¿es posible restringir las instancias de clase para usar solo como temporales?

por ejemplo, tengo la clase Wrapper cuyo constructor toma A const& y guarda esta referencia en su miembro. Es peligroso porque la duración de la instancia Wrapper no puede ser más larga que la duración de la instancia A, pero está bien si Wrapper es .

+1

¿Estás de acuerdo? ¿Es posible solo permitir que se creen instancias en la pila en lugar de en el montón? –

+0

no, solo como provisionales. todavía es peligroso crear tales instancias en la pila –

+1

A veces, el mejor preventivo es un comentario de "do not do X" en la documentación. –

Respuesta

3

no creo que sería seguro:

const A &a = YourClass(tmp); 

YourClass en este caso es la clase que usted está buscando, que sólo permiten instancias temporales, tmp es el valor temporal se pasa al constructor.
Es posible (es decir, seguro, comportamiento definido) tener una referencia constante a un temporal (es decir: a), pero el propio temporal (tal instancia de YourClass) tiene una referencia a tmp que ya no es válido después de esa expresión es evaluado

+0

Exactamente lo que estaba pensando. Puede hacerlo sintácticamente, pero el hecho de que pueda enlazar const-references a él extenderá su duración de todos modos de una manera peligrosa. – CashCow

0

Sí, podría.

Haría que el constructor y el constructor de copia regular/asignaran privado pero harían pública la semántica de movimiento r-value (C++ 0x).

Tendría un constructor estático o amigo para crear el temporal.

En 2003 C++ también podría usar esto para enlazar a una referencia constante.

Por supuesto, usted tendría el problema de que su referencia const probablemente se invalidaría después de la declaración.

+0

Uhm ... ¿Estás seguro de que va a funcionar? Si el único constructor público es el de referencia rvalue, no creo que pueda instanciar ninguna instancia de su clase, por lo que nunca podrá pasar una instancia al constructor de referencia rvalue. – peoro

+0

Sugirió usando una función amiga que devuelve un valor r. – Puppy

+2

Esto no evita 'T const & ref = T :: factory (blah);': la semántica del movimiento permite un retorno por valor (que se puede elidir) y aumenta la vida útil. ¿Qué me estoy perdiendo? –

1

no molestaría hacer cumplir esta en tiempo de compilación, ya que siempre va a haber casos de esquina cuando ello sea excesivamente restrictiva, lo que limita la utilidad de la clase, sino más bien envolver herramientas como valgrind o Purify para que pueda detectar lugares donde se utilizan referencias invalidadas.

3

Creo que incluso querer hacer esto es una señal de un diseño realmente malo.

Sin embargo, puede hacer todos los constructores privados y crear una función de amigo que devuelva un valor r. Eso debería hacer el truco.

+0

¿Cómo funcionaría? O bien el copy ctor o move ctor deben estar accesibles para regresar por valor, incluso si se eliminan. –

+0

No si toma el resultado por referencia. – Puppy

+0

Incluso al tomar el resultado por referencia: http://codepad.org/97oaJNfl y N3225 §12.2p1, "... vinculando una referencia a un valor pr, devolviendo un prvalue ... Incluso si no se llama al constructor copia/movimiento, todos los las restricciones semánticas, como la accesibilidad, deben ser satisfechas ". –

3

No es exactamente la respuesta que está buscando, pero ¿ha pensado en los indicadores débiles? (por ejemplo, boost::weak_ptr). En este caso, el A original se mantendría en un shared_ptr y el constructor Wrapper acepta un weak_ptr. Lo bueno de este enfoque es que, antes de cada uso del weak_ptr, puede intentar lock() que le dará un shared_ptr - si eso falla, usted sabe que A se ha ido y Wrapper no puede funcionar ... Pero se maneja limpiamente. ..

0

Esto podría hacer el trabajo a menos que su clase tenga miembros de datos públicos.

Básicamente, la idea no es restringir la construcción de la envoltura, sino asegurarse de que las instancias pueden ser utilizadas (como usted dijo) solo mientras sean valores temporales.Se puede lograr esto al sobrecargar todos los métodos y eliminarlos (o hacerlos privados) los que se refieren a const &.

Aquí está un ejemplo sencillo:

class Wrapper 
{ 
public: 
    Wrapper() = default; 
    Wrapper(const std::string& name) : name(name) {} 
    void process() && { std::cout << "Greetings from " << name << std::endl; } 
    // Only temporary instances of this class are allowed! 
    void process() const & = delete; 

private: 
    std::string name; 
}; 

Y algunos casos de uso:

Wrapper("John").process(); // intended use case 
Wrapper j; // create whatever you want 
j.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function 
std::move(j).process(); // this is still possible 
const Wrapper& t = Wrapper(); // bind the temporary to a const reference - not a problem because ... 
t.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function 

Las desventajas obvias son:

  • Tienes que sobrecargar cada función miembro pública.
  • El mensaje de error está retrasado y no es muy informativo.

Algo similar se ha hecho en la norma. Las rutinas make para std :: reference_wrapper do not accept temporaries.

Nota que consideraban otra sutileza: la sobrecarga utiliza const T & & en lugar de T & &. Esto también puede ser importante en nuestro caso. Por ejemplo, si su envoltura está diseñada deliberadamente para ser noncopyable y utiliza realizar rutinas tales como

const Wrapper make_wrapper(); 

en lugar de

Wrapper make_wrapper(); 

En este caso, es posible que desee reemplazar

void process() &&; 

por

void process() const &&; 
Cuestiones relacionadas