Pregunta

pena por una pregunta tan larga, pero trato de ser lo más claro posible. Esto de alguna manera sigue mi pregunta anterior sobre cadenas en C ++ . Estoy tratando de averiguar cómo podía volver std :: string de una función sin asignaciones de memoria redundantes, sin depender de NRVO . Las razones por las que no quiero depender de NRVO son:

  • no está soportado por el compilador que utilizamos actualmente
  • incluso cuando se admite que no siempre puede ser activado en el modo de depuración
  • que podría fallar en algunos casos ( ejemplo )

Tenga en cuenta que necesito una solución compatible ++ 03 C (sin referencias C ++ 0x rvalue por lo tanto, por desgracia ...)

La forma más sencilla de hacer esto es el paso por referencia y no std :: swap, como esto

void test(std::string& res)
{
    std::string s;
    //...
    res.swap(s);
}

Sin embargo, es más natural y, a menudo más conveniente para volver por el valor que pase por referencia, así que lo que quiero lograr es la siguiente:

std::string test()
{
    std::string s;
    //...
    return SOMETHING(s);
}

Lo ideal sería simplemente hacer una swap con el "valor de retorno", pero no veo cómo hacer esto en C ++. Hay auto_ptr ya la que se mueve en lugar de copiar, y yo en realidad podría utilizar auto_ptr<string>, pero me gustaría evitar asignar dinámicamente el propio objeto de cadena.

Mi idea es de alguna manera "etiqueta" un objeto de cadena que se está volviendo de una función para permitir en movimiento sus datos cuando un constructor de copia se llama a la vuelta. Así que terminé con este código, que hace exactamente lo que quiero:

struct Str
{
    struct Moveable
    {
        Str & ref;
        explicit Moveable(Str & other): ref(other) {}
    };

    Str() {}
    Str(const std::string& other) : data(other) {} // copy
    Str(Moveable& other) { data.swap(other.ref.data); } // move

    Moveable Move()
    {
        return Moveable(*this);
    }

    std::string data;
};

Str test()
{
    Str s;
    //...
    return s.Move(); // no allocation, even without NRVO
}

Así que ... ¿Todo esto tiene sentido, o hay algunos problemas graves que me falta? (No estoy seguro de que si no hay ningún problema de por vida, por ejemplo). Tal vez ya han visto tal idea en una biblioteca (libros, artículos ...), y me podría dar una referencia a él?

EDIT: Como @rstevens notó, este código es MSVC-específica y no se compilará en virtud g ++, que no le gusta el temporal no constante. Este es un problema, pero vamos a asumir esta aplicación es MSVC específica.

¿Fue útil?

Solución

The implementation of boost uses move semantics emulation internally for libraries like Boost.Thread. You may want to look at the implementation and do something similar.

Edit: there is actually an active development of a library Boost.Move, so you can already start using it.

Otros consejos

Did you check this code on g++?

Since you call Str(Movable &) with a temporary object (the one returned by s.Move())!

This is not standard's compliant and not supported by g++. It is supported by MSVC! (MS calls this a feature...).

Have you actually determined that returning by value is a performance problem in your application? That seems like the simplest/easiest way to go, and when you upgrade to a more modern compiler you can use rvalue references.

I can't answer the question regarding the destruction order of s vs the Movable's reference. You could, for your compiler, put code in the various constructors and destructors to see what the order is. Even if it looks ok I'd still consider using one of the normal patterns you outlined though just to prevent reader confusion and possibly breaking on an alternate compiler.

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