¿Cómo eliminar un elemento de un vector cuando el objeto contenedor contiene un unique_ptr?

Noté que no es posible eliminar un elemento de un vector cuando el tipo contenedor contiene un unique_ptr.

Por ejemplo esta clase:

class Bar
    {
        std::unique_ptr<int> pointerTest;

        Bar(Bar &bar) {};

    public:
        Bar() { pointerTest = std::unique_ptr<int>(new int); }
        Bar(Bar &&bar) { this->pointerTest = move(bar.pointerTest); }

        void testFunc() { pointerTest.release(); }
    };

Esto no funciona con este uso:

int main()
    {
        vector<Bar> test123;

        Bar foo;
        test123.push_back(move(foo));
        test123.erase(test123.begin());
    }

    //Error 1   error C2280: 'std::unique_ptr<int,std::default_delete<_Ty>> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function c:\program files (x86)\microsoft visual studio 12.0\vc\include\xmemory0 611 1   testing

El código funcionará cuando solo llame a los métodos de objetos en lugar de borrar:

for (auto &item : test123)
    item.testFunc();

¿Cuál es la razón por la que no puedo usar el borrado en el vector y cómo podría resolverlo?

preguntado el 22 de noviembre de 13 a las 15:11

FWIW, su código se compila bajo g ++ 4.6. -

Puedo arreglar esto en GCC 4.8 eliminando el constructor de movimientos innecesarios y el constructor de copias extrañas. No sé exactamente cuál es el problema, o si eso lo solucionará para su compilador. -

Extraño, no puedo compilar esto en Visual Studio 2013 con o sin el constructor de copiar y mover. @Mike Seymour ¿Qué está conectado con esos constructores? -

el error es claro, intenta llamar al operador de asignación de unique_ptr que toma una referencia const lvalue, que se elimina de unique_ptr. su código nunca lo llama, tal vez el problema esté en algún lugar de su código que no muestra. quizás el destructor de Bar. -

@yngum El código en esta pregunta es el código completo que compilo. Puedes encontrarlo aquí también: pastebin.com/jHNgSUi9. -

2 Respuestas

El problema es que tu clase no sigue correctamente la regla del tres/cinco. Defina un constructor de copiar y mover, pero no un operador de asignación de movimiento. Lo que significa que no se genera ningún operador de asignación de movimiento y el operador de asignación de copia predeterminado siempre se usa al asignar. Sin embargo, dado que su clase tiene un miembro no copiable, el operador de asignación de copia predeterminado se define como eliminado, por lo que obtiene un error al usarlo.

Y se invoca porque la eliminación de elementos de un vector provoca un desplazamiento (es decir, asignación) de todos los elementos siguientes del vector. Esa sería una asignación de movimiento normalmente, pero dado que eso no está definido (y no se genera automáticamente) en su clase, se invoca la asignación de copia y falla.

Veo que está utilizando Visual Studio, por lo que un constructor de movimiento/operador de asignación de movimiento no se generaría automáticamente de todos modos (VS aún no es compatible con esta parte del estándar). Entonces, para solucionar esto de una manera compatible con VS, defina un operador de asignación de movimiento. Y podría ser una buena idea eliminar el constructor de copia mientras lo hace: la clase parece un excelente ejemplo de una clase no copiable.

respondido 22 nov., 13:16

El mensaje de error de g ++ 4.8.1 es bastante claro:

trash.cpp:9:11: nota: 'Bar& Bar::operator=(const Bar&)' se declara implícitamente como eliminado porque 'Bar' declara un constructor de movimiento o un operador de asignación de movimiento

Al agregar el operador de movimiento, el problema se resuelve:

class Bar
{
        std::unique_ptr<int> pointerTest;

        Bar(const Bar &);

public:
        Bar() { pointerTest = std::unique_ptr<int>(new int); }
        Bar(Bar &&bar) : pointerTest( move(bar.pointerTest)) {}
        Bar& operator=(Bar&& bar){ pointerTest = std::move(bar.pointerTest); return *this;}

        void testFunc() { pointerTest.release(); }
};

respondido 22 nov., 13:16

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.