2011-01-19 15 views
8

lo siento por una pregunta tan larga, pero trato de ser lo más claro posible. Esto de alguna manera sigue mi pregunta anterior sobre strings in C++. Estoy intentando descubrir cómo podría devolver std :: string desde una función sin asignaciones de memoria redundante, sin depender de NRVO. Las razones por las que no quiero depender de NRVO son:SWL swap on return?

  • no es apoyada por el compilador Actualmente utilizamos
  • incluso cuando se admite que no siempre puede ser activado en el modo de depuración
  • que podría fallar en algunos casos (example)

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

el SI mplest manera de hacer esto es pasar por referencia y hacer std :: swap, como esto

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

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

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

lo ideal sería que sólo haría un swap con el "valor de retorno", pero no veo cómo hacer esto en C++. Ya hay auto_ptr que se mueve en lugar de copiar, y realmente podría usar auto_ptr<string>, pero me gustaría evitar asignar dinámicamente el objeto de cadena en sí.

Mi idea es "etiquetar" de algún modo un objeto de cadena que se devuelve desde una función para permitir mover sus datos cuando se llama a un constructor de copia en el retorno. 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 
} 

Entonces ... ¿Todo esto tiene sentido, o hay algunos problemas graves que me falta? (No estoy seguro si no hay un problema de por vida, por ejemplo). Tal vez ya has visto esa idea en una biblioteca (libro, artículo ...), ¿y podría darme una referencia?

EDITAR: Como @rstevens notó, este código es específico de MSVC y no se compilará en g ++ que no le gusta el no const temporal. Este es un problema, pero supongamos que esta implementación es específica de MSVC.

+0

No estoy seguro de lo que estás preguntando. ¿Solo necesitas arreglar tu código? Usted dice que hace exactamente lo que quiere pero, por lo que puedo ver, no debería compilarse. –

+2

¿Ha comprobado si su cadena hace COBERTURA? Si es así, habrá pocas asignaciones de memoria adicionales. También algunas implementaciones de std :: string colocarán la cadena en el objeto (cuando la cadena es corta) en lugar de asignarle memoria. Si alguno de estos factores es cierto, sus intentos de optimización pueden aumentar el costo en lugar de reducirlo. –

+0

@Charles: el código está ahí para explicar lo que estoy tratando de lograr, y la pregunta es si lo veo bien y si ya hay algunas buenas implementaciones de eso. –

Respuesta

4

La implementación de boost utiliza emulación de semántica de movimiento internamente para bibliotecas como Boost.Thread. Es posible que desee ver la implementación y hacer algo similar.

Editar: En realidad, hay un desarrollo activo de una biblioteca Boost.Move, entonces ya puede comenzar a usarlo.

0

¿De hecho ha determinado que devolver por valor es un problema de rendimiento en su aplicación? Esa parece ser la forma más sencilla/más fácil de hacerlo, y cuando se actualiza a un compilador más moderno, puede usar referencias rvalue.

No puedo responder la pregunta con respecto a la orden de destrucción de s frente a la referencia de Movable. Podría, para su compilador, poner código en varios constructores y destructores para ver cuál es el orden.Incluso si se ve bien, aún consideraría usar uno de los patrones normales que usted describió para evitar la confusión del lector y posiblemente romper con un compilador alternativo.

+1

No solo es una cuestión de optimización, ya que influye en el uso típico: a menos que los desarrolladores sepan que el retorno por valor es barato, intentarán evitar hacerlo. La gente aún intenta escribir un código algo óptimo, incluso si dices que la optimización prematura es mala. –

1

¿Has marcado este código en g ++?

Dado que llama a Str (Movable &) con un objeto temporal (el devuelto por s.Move())!

Esto no es compatible con las normas y no es compatible con g ++. ¡Es compatible con MSVC! (MS lo llama una característica ...).

+0

Tiene razón, no compilará en g ++, he actualizado la pregunta. Probablemente se pueda hacer algo diferente para g ++, pero estoy más interesado en saber si algo * similar * se usa en alguna parte, no exactamente mi implementación de muestra. –