Std :: unique_ptr remote function, initializer_list - managed distribution

Everything,

When I create an instance of the widget array using the format of the initializer list, a bare pointer that points to the widget instance of the member variable, but after changing to std :: unique_ptr <> gcc gives a compilation error regarding the remote function.

$ uname -a

Linux .. 3.5.0-21-generi # 32-Ubuntu SMP Tue Dec 11 18:51:59 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux

$ g ++ --version

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

This code gives the following compiler error:

#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<> } 

Error:

 $ 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] 

I find it difficult either: the mechanics behind the scenes, which gives the remote fcxn; or more simply, why the expressiveness of std :: unique_ptr <> is limited compared to naked ptr.

My question is:

  • pilot error
  • Compiler error?
  • Can I make my intended code work with some changes?

Thanks.

Change 1

Based on your answers, which I appreciate, I can make the following changes to WFactory:

( marked as immoral code )

 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; }; 

and now the program compiles and runs. I appreciate that the people in charge of the standards wrote the specification for some reason, so I publish my result as a bona fide specialization for my business, where I really would like to emphasize the uniqueness of ptr.

Edit 2

Based on Jonathan's answers, the following code does not suppress the implicit ctor move:

 class WFactory { public: WFactory(const int i) : _w(new W1()) {} WFactory(const char* s) : _w(new W2()) {} private: std::unique_ptr<Widget> _w; }; 

Note that there is no ~WFactory() {..} at all.

There may be ya-ans, but I found that using C ++ 11 wf [] style iteration in Main () returns a no-copy-ctor-for-WFactory error. I.e:

 int Main() .. WFactory wf[] { 4, "msg" }; for ( WFactory iwf : wf ) <---- compiler error again // .. for (unsigned i = 0; i < 2; ++i) <--- gcc happy wf[i] // .. } 

I assume it goes without saying that the new C ++ 11 style iteration makes a copy of the object.

+6
source share
2 answers

According to clause 8.5.1 / 2 of the C ++ 11 standard:

When an aggregate is initialized with a list of initializers, as specified in 8.5.4, the elements of the list of initializers are accepted as initializers for members of the aggregate, increasing the index or order of members. Each member is copy-initialized from the corresponding initializer clause. [...]

For each element, then, copy initialization involves creating a temporary destination type, which is then used to copy-construct the array element.

However, your class contains a member whose type is an instance of unique_ptr that is not copied. This makes your class not copied.

Also, although unique_ptr is movable, your class is not, because the implicit generation of a move constructor by the compiler is suppressed by the presence of an explicitly defined destructor. If this is not the case (i.e., if you explicitly defined a move constructor for your class), then copy-initialization will be performed (see 8.5 / 15).

Try changing the definition of WFactory as follows to see that:

 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 } 
+8
source

mechanics behind the scenes that gives remote fcxn;

Arrays can only be initialized this way if the type is copyable or movable, and unique_ptr not copied, so the class with the unique_ptr element unique_ptr not copied by default, and your type has a user-defined destructor that blocks the implicit move constructor, so your type also does not move .

or more simply, why the expressiveness of std::unique_ptr<> is limited compared to bare ptr.

unique_ptr saves you from a serious mistake. Using an open pointer, your type is massively unsafe and will lead to undefined behavior, because you do not have a copy constructor so that the pointer is copied and then deleted twice by two different objects. Boom, your program has undefined behavior. unique_ptr fixes your class, preventing it from being copied, which is safe and correct.

You can make it work in several ways, the easiest way is to remove a user-defined destructor that will make your class movable and array initialization will be compiled.

Or, if you need a user-defined destructor for some other reason, you can still make it work by writing a user-defined move constructor to explicitly move the _w element.

If for some reason this is not possible, you can make it work by adding a default constructor, so the elements of the array can be configured by default, and then move them:

 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"); } 

Your edited version is immoral and very doubtful, you should not cast away const like this, and you should not switch from lvalue, especially not to const lvalue. Do not go there. Instead, change the way you use the class to avoid having to copy it, or write a valid copy constructor that makes a deep copy of its object:

 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()) {} // ... 

The best approach is to make the class movable, and a good way to do this is to follow rule 0

+4
source

All Articles