2013-02-22 5 views
6

Все,станд :: unique_ptr удалена функция, initializer_list - приводится распределение

Когда я создаю экземпляр массива виджетов, используя формат инициализатор-лист, обнаженную указатель, который указывает на экземпляр виджета член-переменная компилируется, но после того, как изменения в std :: unique_ptr <> gcc дает ошибку компиляции относительно удаленной функции.

$ uname -a

Linux .. 3.5.0-21-родовой # 32-Ubuntu SMP Вт 11 декабря 18:51:59 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

$ г ++ - -версия

г ++ (Ubuntu/Linaro 4.7.2-5ubuntu1) 4.7.2

Этот код дает следующее сообщение об ошибке компилятора:

#include <stdlib.h> 
#include <memory> 

class Widget 
{ 
public: 
    Widget() {} 
}; 

class W1 : public Widget 
{ 
public: 
    W1() {} 
}; 

class W2 : public Widget 
{ 
public: 
    W2() {} 
}; 

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

    ~WFactory() { _w.reset(nullptr); } 
    // ~WFactory() { delete _w; } <--- for naked ptr 

private: 
    // NOTE: does not compile 
    std::unique_ptr<Widget> _w; 
    // NOTE: does compile 
    // Widget* _w; 
}; 

int main() 
{ 
    std::unique_ptr<Widget> a(new W1()); // <--- compiles fine 

    WFactory wf[] { 4, "msg" };   // <--- compiler error using unique_ptr<> 
} 

ошибка:

$ g++ -o unique_ptr -std=c++11 -Wall unique_ptr.cpp 
unique_ptr.cpp: In function ‘int main()’: 
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’ 
unique_ptr.cpp:22:7: note: ‘WFactory::WFactory(const WFactory&)’ is implicitly deleted because the default definition would be ill-formed: 
unique_ptr.cpp:22:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Widget; _Dp = std::default_delete<Widget>; std::unique_ptr<_Tp, _Dp> = std::unique_ptr<Widget>]’ 
In file included from /usr/include/c++/4.7/memory:86:0, 
      from unique_ptr.cpp:2: 
/usr/include/c++/4.7/bits/unique_ptr.h:262:7: error: declared here 
unique_ptr.cpp:36:30: error: use of deleted function ‘WFactory::WFactory(const WFactory&)’ 
unique_ptr.cpp:36:14: warning: unused variable ‘wf’ [-Wunused-variable] 

Я в недоумении, либо: механика за кулисами, что дает удаленный fcxn; или более просто, почему выразительность std :: unique_ptr <> оказывается ограниченной по сравнению с голым ptr.

Мой вопрос:

  • ошибка пилота?
  • ошибка компилятора?
  • Могу ли я заставить свой предназначенный код работать с некоторыми изменениями?

спасибо.

Edit 1

Исходя из ваших ответов, которые я ценю, я могу сделать следующее изменение WFactory:

(помечен как аморальный код)

class WFactory 
{ 
public: 
    WFactory(const WFactory& wf) 
    { 
     (const_cast<WFactory&>(wf)).moveto(_w); 
    } 

    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

    ~WFactory() { _w.reset(nullptr); } 

    void moveto(std::unique_ptr<Widget>& w) 
    { 
     w = std::move(_w); 
    } 
private: 
    std::unique_ptr<Widget> _w; 
}; 

и теперь программа компилируется и запускается. Я высоко ценю, что люди, отвечающие за стандарты, написали спецификацию по какой-либо причине, поэтому я публикую свой результат как добросовестную специализацию для моего дела, где я действительно хотел бы подчеркнуть уникальность ptr.

Edit 2

На основе ответов Джонатана, следующий код не подавляет неявное перемещение CTOR:

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

private: 
    std::unique_ptr<Widget> _w; 
}; 

Обратите внимание, что нет никакого ~WFactory() {..} вообще.

Возможно, есть ya-ans, но я обнаружил, что использование итерации C++ 11 в стиле wf [] в Main() возвращает ошибку no-copy-ctor-for-WFactory.То есть:

int Main() 
.. 
    WFactory wf[] { 4, "msg" }; 

    for (WFactory iwf : wf) <---- compiler error again 
     // .. 

    for (unsigned i = 0; i < 2; ++i) <--- gcc happy 
     wf[i] // .. 
} 

Я предполагаю, что это само собой разумеется, что новый C++ 11-стиль итерация делает объект копию.

+0

Инициализация через скошенные списки требует, к сожалению, объектов, подлежащих форматированию. –

+0

Спасибо, это интересно. Где копия? Ошибка указывает, что WFactory должен быть скопирован. Я этого не понимаю. – JayInNyc

+0

Это в спецификации языка. Копия фактически не будет на практике, но класс все равно должен иметь доступный конструктор копирования. –

ответ

8

В соответствии с пунктом 8.5.1/2 C++ 11 стандарта на:

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer listare taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause. [...]

Для каждого элемента, а затем, копирование инициализации включает в себя создание временного типа назначения, который затем использовать для копирования-построения элемента массива.

Однако ваш класс содержит элемент, тип которого является экземпляром unique_ptr, который не подлежит копированию. Это делает ваш класс не скопированным.

Кроме того, хотя это unique_ptrперемещаемого, ваш класс не является, так как неявное поколение конструктора перемещения компилятора подавляется присутствием явно заданный деструктор. Если это не так (т. Е. Если вы явно определили конструктор перемещения для своего класса), то будет выполнена инициализация копирования (см. 8.5/15).

Попробуйте изменить определение WFactory следующим образом, чтобы видеть, что:

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 
    WFactory(WFactory&& f) : _w(std::move(f._w)) {} 
    ~WFactory() { _w.reset(nullptr); } 
private: 
    std::unique_ptr<Widget> _w; 
}; 

int main() 
{ 
    std::unique_ptr<Widget> a(new W1()); 
    WFactory wf[] { 4, "msg" };   // OK 
} 
+0

См. Править выше. Еще раз спасибо. – JayInNyc

+0

[dcl.init]/15 "[_Примечание: _ Копировальная инициализация может вызвать ход (12.8). - _end note] _" –

+0

@JonathanWakely: Очень правильно. Позвольте мне изменить. Спасибо. –

4

the mechanics behind the scenes that yields a deleted fcxn;

Массивы могут быть инициализированы только так, если тип копируемой или подвижный и unique_ptr не копируемыми, поэтому класс с членом unique_ptr по умолчанию не может быть скопирован, и ваш тип имеет определяемый пользователем деструктор, который блокирует неявный механизм перемещения, поэтому ваш тип также не перемещается.

or more simply, why the expressiveness of std::unique_ptr<> appears to be restricted compared w/ a naked ptr.

unique_ptr спасает вас от серьезной ошибки. С помощью открытого указателя ваш тип является массово небезопасным и приведет к неопределенному поведению, потому что у вас нет конструктора копирования, чтобы указатель копировался и затем дважды удалялся двумя разными объектами. Boom, ваша программа имеет неопределенное поведение. unique_ptr исправляет ваш класс, предотвращая его копирование, что является безопасным и правильным.

Вы можете заставить его работать несколькими способами, проще всего удалить определяемый пользователем деструктор, который сделает ваш класс подвижным и инициализация массива будет скомпилирована.

Если вам нужен определенный пользователем деструктор по какой-либо другой причине, вы все равно можете заставить его работать, написав определяемый пользователем конструктор перемещения, чтобы явно перемещать элемент _w.

Если по какой-то причине невозможно, вы можете заставить его работать, добавив конструктор по умолчанию, так что элементы массива могут быть построены по умолчанию, а затем двигаться, назначая им:

class WFactory 
{ 
public: 
    WFactory() = default; 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 

private: 
    std::unique_ptr<Widget> _w; 
}; 

int main() 
{ 
    WFactory wf[2]; 
    wf[0] = WFactory(4); 
    wf[1] = WFactory("msg"); 
} 

Ваш отредактированная версия является аморальной и очень сомнительной, вы не должны отбрасывать const, и вам не следует перемещаться с lvalue, особенно не const lvalue. Не иди туда.Вместо того, чтобы изменить то, как вы используете класс, чтобы избежать необходимости копировать его, или написать правильный конструктор копирования, который делает глубокую копию принадлежащего объекта:

class Widget 
{ 
public: 
    Widget() {} 
    virtual std::unique_ptr<Widget> clone() const = 0; 
}; 

class W1 : public Widget 
{ 
public: 
    W1() {} 
    virtual std::unique_ptr<Widget> clone() const 
    { return std::unique_ptr<Widget>(new W1(*this)); } 
}; 

class W2 : public Widget 
{ 
public: 
    W2() {} 
    virtual std::unique_ptr<Widget> clone() const 
    { return std::unique_ptr<Widget>(new W2(*this)); } 
}; 

class WFactory 
{ 
public: 
    WFactory(const int i) : _w(new W1()) {} 
    WFactory(const char* s) : _w(new W2()) {} 
    WFactory(const WFactory& w) : _w(w._w->clone()) {} 
    // ... 

Наилучшим подходом является сделать класс движимым и хороший способ сделать это - следовать за rule of zero

+0

Мне нравится, что мое редактирование безнравственно ;-) Это положило улыбку на мое лицо (действительно). – JayInNyc

+0

«С открытым голосом ваш тип является массово опасным и приведет к неопределенному поведению, потому что у вас нет конструктора копирования, поэтому указатель копируется, а затем дважды удаляется двумя разными объектами. Boom, ваша программа имеет неопределенное поведение. Unique_ptr исправляет ваш класс, предотвращая его копирование, что является безопасным и правильным ». - поэтому мы согласны. Это вдохновение для моего первоначального вопроса. – JayInNyc

+0

FYI мое приложение не может последовательно вызвать wf [0] = ..., wf [1] = ... wf [] {..} - это механизм I (требуется). – JayInNyc