Передача права собственности в векторе unique_ptrs

У меня 2 класса А и В

//A.h
class A{};

// B.h
typedef unique_ptr<A, AllocateA> APtr;
typedef vector<APtr> BVEC;

class B
{
public:
   BVEC vec; //error is here
   //....
};

Когда я компилирую код, я получаю unique_ptr....attempting to reference a deleted function

Затем я добавляю конструктор копирования и оператор присваивания к классу B, как это

class B
{
public:
   BVEC vec; //error is here
   //....
   B& operator=(B&b);
   B(B&b);
};

Но я все еще получаю то же сообщение об ошибке.


person user0175554    schedule 29.01.2015    source источник
comment
unique_ptr не может быть скопирован. Попробуйте shared_ptr.   -  person Paul Rooney    schedule 29.01.2015
comment
@ user0175554 Что такое AllocateA? Пожалуйста, скажите мне, что это не то, что вы назвали своим удаляющим средством.   -  person Jonathan Mee    schedule 29.01.2015
comment
У вас не может быть конструктора копирования, если вы хотите передать право собственности. Операция копирования должна оставлять оригинал без изменений.   -  person molbdnilo    schedule 29.01.2015
comment
@ user015554: как сказал другой: вы не можете скопировать unique_ptr, но вы можете move это :)   -  person Michał Walenciak    schedule 29.01.2015
comment
@MichałWalenciak Я уверен, что вы подразумеваете это, но просто для ясности: не move переменная-член в конструкторе копирования. Сделайте это в конструкторе перемещения.   -  person Jonathan Mee    schedule 29.01.2015
comment
Почему заголовок вашего вопроса относится к общим указателям? unique_ptr не то же самое, что shared_ptr   -  person Jonathan Wakely    schedule 29.01.2015
comment
Ваш конструктор копирования и оператор присваивания должны принимать const B& параметров.   -  person Jonathan Wakely    schedule 29.01.2015
comment
@JonathanMee Почему?   -  person user0175554    schedule 29.01.2015
comment
@user0175554 user0175554 Почему AllocateA удаляет? Потому что это ваш второй аргумент шаблона для unique_ptr: cplusplus.com/reference/memory/unique_ptr /#parameters Мне интересно, не предполагалось ли, что AllocateA будет вторым аргументом шаблона для vector в вашем BVEC typedef, поскольку vector действительно принимает распределитель в качестве второго параметра шаблона.   -  person Jonathan Mee    schedule 29.01.2015


Ответы (2)


Этот код отлично работает как в gcc 4.9.2, так и в Visual Studio 2013:

#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>

using namespace std;

//A.h
class A{
public:
    int alpha;
    A(int input) : alpha(input){}
};

// B.h
typedef unique_ptr<A> APtr;
typedef vector<APtr> BVEC;

class B
{
public:
    BVEC vec;
    B(){}
    const B& operator=(const B& b){
        vec.clear();
        for_each(b.vec.cbegin(), b.vec.cend(), [&](const unique_ptr<A>& i){vec.push_back(unique_ptr<A>(new A(*i))); });
        return b;
    }
    B(const B& b){
        vec.clear();
        for_each(b.vec.cbegin(), b.vec.cend(), [&](const unique_ptr<A>& i){vec.push_back(unique_ptr<A>(new A(*i))); });
    }
    const B& operator=(B&& b){
        vec.resize(b.vec.size());
        move(b.vec.begin(), b.vec.end(), vec.begin());
        return *this;
    }
    B(B&& b){
        vec.resize(b.vec.size());
        move(b.vec.begin(), b.vec.end(), vec.begin());
    }
};

int main() {
    B foo;
    B bar;

    for (auto i = 0; i < 10; ++i){
        foo.vec.push_back(unique_ptr<A>(new A(i)));
    }
    bar = foo;
    foo.vec.clear();

    for (auto& i : bar.vec){
        cout << i->alpha << endl;
    }
    foo = move(bar);

    for (auto& i : foo.vec){
        cout << i->alpha << endl;
    }
    return 0;
}

Я не знаю, что вы использовали для удаления в APtr. (Я задал вопрос в комментариях, но еще не видел ответа.) Я подозреваю, что если вы правильно написали свой конструктор копирования для B и конструктор копирования для A, то ваша проблема связана с вашим средством удаления, AllocateA.

Вы можете видеть в конструкторах копирования, которые я написал для B, я динамически создаю идентичные A в this.vec для каждого A в b.vec. Я предполагаю, что это поведение, которое вы хотите. Если вы хотите просто переместить динамические распределения, я бы предложил использовать конструктор перемещения >как предложил Михал Валенсиак.

EDIT: После просмотра названия OP я почувствовал, что, возможно, это был конструктор перемещения. Поэтому я добавил один из них.

person Jonathan Mee    schedule 29.01.2015
comment
Спасибо, без AllocateA моя программа не будет работать. Я использую shared_ptr плюс ваш код, но я также согласен с тем, что ваш ответ правильный, за исключением моего собственного случая. - person user0175554; 29.01.2015

Это потому, что unique_ptr ... уникальны, вся суть в том, что они указывают на объект, и когда unique_ptr выходит за рамки — он удаляет переменную, на которую указывает. если бы вы могли легко присвоить указанную переменную другому unique_ptr, когда указанная переменная будет удалена? когда первый выходит за рамки или второй включается? тут нет никакой "уникальности".

вот почему копирование или назначение unique_ptr не разрешено, копировщик и оператор присваивания отключены

вы ищете shared_ptr . несколько shared_ptr могут указывать на одну переменную, и она удаляется, когда ВСЕ из них выходят из области видимости, какой-то примитивный сборщик мусора

person David Haim    schedule 29.01.2015
comment
@MichałWalenciak Что соответствовало бы его заголовку: «Передача права собственности», но не в соответствии с конструктором копирования. - person Jonathan Mee; 29.01.2015
comment
Спасибо, сэр, @user3613500, это, как всегда, очень хорошее объяснение. - person user0175554; 29.01.2015