¿Vida útil garantizada de temporal en C++? [duplicar]

Resuelto Mark Ransom asked hace 15 años • 6 respuestas

¿C++ proporciona una garantía durante la vida útil de una variable temporal que se crea dentro de una llamada de función pero que no se utiliza como parámetro? Aquí hay una clase de ejemplo:

class StringBuffer
{
public:
    StringBuffer(std::string & str) : m_str(str)
    {
        m_buffer.push_back(0);
    }
    ~StringBuffer()
    {
        m_str = &m_buffer[0];
    }
    char * Size(int maxlength)
    {
        m_buffer.resize(maxlength + 1, 0);
        return &m_buffer[0];
    }
private:
    std::string & m_str;
    std::vector<char> m_buffer;
};

Y así es como lo usarías:

// this is from a crusty old API that can't be changed
void GetString(char * str, int maxlength);

std::string mystring;
GetString(StringBuffer(mystring).Size(MAXLEN), MAXLEN);

¿Cuándo se llamará al destructor del objeto temporal StringBuffer? Lo es:

  • ¿Antes de la llamada a GetString?
  • ¿Después de que regrese GetString?
  • ¿Depende del compilador?

Sé que C++ garantiza que una variable temporal local será válida siempre que haya una referencia a ella. ¿Se aplica esto a los objetos principales cuando hay una referencia a una variable miembro?

Gracias.

Mark Ransom avatar Feb 25 '09 12:02 Mark Ransom
Aceptado

El destructor para ese tipo de temporales se llama al final de la expresión completa. Esa es la expresión más externa que no forma parte de ninguna otra expresión. Eso es en su caso después de que la función regresa y se evalúa el valor. Entonces todo funcionará bien.

De hecho, es lo que hace que las plantillas de expresión funcionen: pueden mantener referencias a ese tipo de temporales en una expresión como

e = a + b * c / d

Porque cada temporal durará hasta la expresión.

x = y

Se evalúa por completo. Se describe de manera bastante concisa en 12.2 Temporary objectsel Estándar.

Johannes Schaub - litb avatar Feb 25 '2009 05:02 Johannes Schaub - litb

La respuesta de litb es precisa. La vida útil del objeto temporal (también conocido como rvalue) está ligada a la expresión y el destructor del objeto temporal se llama al final de la expresión completa y cuando se llama al destructor en StringBuffer, el destructor en m_buffer también será llamado, pero no el destructor en m_str ya que es una referencia.

Tenga en cuenta que C++ 0x cambia las cosas un poco porque agrega referencias de valor y semántica de movimiento. Básicamente, al usar un parámetro de referencia de rvalue (anotado con &&), puedo 'mover' el rvalue a la función (en lugar de copiarlo) y la vida útil del rvalue se puede vincular al objeto al que se mueve, no a la expresión. Hay una publicación de blog realmente buena del equipo de MSVC que explica esto con gran detalle y animo a la gente a leerla.

El ejemplo pedagógico para mover rvalue son cadenas temporales y mostraré la asignación en un constructor. Si tengo una clase MyType que contiene una variable miembro de cadena, se puede inicializar con un valor r en el constructor de esta manera:

class MyType{
   const std::string m_name;
public:
   MyType(const std::string&& name):m_name(name){};
}

Esto es bueno porque cuando declaro una instancia de esta clase con un objeto temporal:

void foo(){
    MyType instance("hello");
}

lo que sucede es que evitamos copiar y destruir el objeto temporal y "hola" se coloca directamente dentro de la variable miembro de la instancia de la clase propietaria. Si el objeto pesa más que una 'cadena', entonces la copia adicional y la llamada al destructor pueden ser significativas.

Rick avatar Feb 25 '2009 06:02 Rick

Después de que regrese la llamada a GetString.

David Segonds avatar Feb 25 '2009 05:02 David Segonds