Home:ALL Converter>std::unique_ptr deleted function, initializer_list - driven allocation

std::unique_ptr deleted function, initializer_list - driven allocation

Ask Time:2013-02-23T04:17:03         Author:JayInNyc

Json Formatter

All,

When I instantiate a widgets array using an initializer-list format, a naked pointer that points to a member-variable widget instance compiles but after change to std::unique_ptr<> gcc gives a compilation error regarding a deleted function.

$ uname -a

Linux .. 3.5.0-21-generic #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'm at a loss as to either: the mechanics behind the scenes that yields a deleted fcxn; or more simply, why the expressiveness of std::unique_ptr<> appears to be restricted compared w/ a naked ptr.

My question is:

  • pilot error?
  • compiler error?
  • can I get my intended code to work w/ some change?

Thank you.

Edit 1

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

(flagged 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 standards folks wrote the specification for a reason, so I post my result as a good-faith specialization for my case at hand, where I really would like to emphasize the uniqueness of the ptr.

Edit 2

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

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.

Perhaps there's ya-ans, but I have found that using a c++11-style iteration over wf[] in Main() brings back the no-copy-ctor-for-WFactory error. That is:

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 guess it's self-evident that the new c++11-style iteration is doing an object copy.

Author:JayInNyc,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/15032501/stdunique-ptr-deleted-function-initializer-list-driven-allocation
Andy Prowl :

According to Paragraph 8.5.1/2 of the C++11 Standard:\n\n\n 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. [...]\n\n\nFor each element, then, copy-initialization involves the creation of a temporary of the destination type, which is then use to copy-construct the element of the array.\n\nHowever, your class contains a member whose type is an instance of unique_ptr, which is non-copyable. That makes your class non-copyable as well. \n\nMoreover, although unique_ptr is moveable, 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 that were not the case (i.e., if you explicitly defined a move constructor for your class), copy-initialization would work (see 8.5/15).\n\nTry changing the definition of WFactory as follows to see that:\n\nclass WFactory\n{\npublic:\n WFactory(const int i) : _w(new W1()) {}\n WFactory(const char* s) : _w(new W2()) {}\n WFactory(WFactory&& f) : _w(std::move(f._w)) {}\n ~WFactory() { _w.reset(nullptr); }\nprivate:\n std::unique_ptr<Widget> _w;\n};\n\nint main()\n{\n std::unique_ptr<Widget> a(new W1());\n WFactory wf[] { 4, \"msg\" }; // OK\n}\n",
2013-02-22T20:29:43
yy