6

I get Compiler Error C2248 when i try to compile the following code:

#include <list>
#include <memory>
using namespace std;


class data
{
public:

    static data parse()
    {
        data d;
        data::parse(d);
        return d;
    }

    list<std::unique_ptr<data>> l;

private:

    static void parse(data& node)
    {       }
};


int main()
{

    return 0;
}

Why? How can i fix this?

Note: I have no problem using std::shared_ptr instead of std::unique_ptr.

Nick
  • 10,309
  • 21
  • 97
  • 201

3 Answers3

10

You need to provide move operations for your type:

data(data&& other)
    : l(std::move(other.l))
{
}

data& operator=(data&& other)
{
    l = std::move(other.l);
    return *this;
}

And, since you'll have added a user-declared constructor, you'll also need a user-declared default constructor:

data() { }

My understanding is that your code is correct as-is, per the final C++11 language standard. Visual C++ does not fully implement the final specification for when move operations are implicitly generated (as of the Visual C++ 2012 RC). The specification for when implicit move operations are generated changed several times very late in the standardization process.

If you have a class type C that has any data member that is movable but noncopyable, Visual C++ will not generate an implicit move constructor or move assignment operator, and the implicit copy constructor and copy assignment operator are both suppressed by the presence of the move-only data member. In other words, if you want ot aggregate move-only types, you must provide the move operations for the aggregating class yourself.

(At least, this is my understanding from experimentation with the compiler.)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • I don't know what are move ctor and move assignment operator and what they are for. Can you explain me? – Nick Jul 03 '12 at 20:26
  • If you want an aggregate, you better say `data() = default;`! – Kerrek SB Jul 03 '12 at 20:27
  • 3
    @KerrekSB: The OP is using Visual C++, which does not support defaulted and deleted special member functions. If one is using a mythical compiler that supports the entirety of C++11 and has no bugs, then none of this is necessary and the code should be fine as-is. – James McNellis Jul 03 '12 at 20:28
  • @JamesMcNellis: I see. That's a shame. – Kerrek SB Jul 03 '12 at 20:29
  • 1
    @Nick: See [Can someone please explain move semantics to me?](http://stackoverflow.com/questions/3106110/can-someone-please-explain-move-semantics-to-me) `std::unique_ptr` is a move-only type: you cannot copy objects of that type, you can only move them. This is because a given `std::unique_ptr` object is the unique owner of a given `T` object (hence it's name). If you could copy it, then it would no longer be the unique owner (there would then be two owners). – James McNellis Jul 03 '12 at 20:32
5

First things first, VC++ doesn't automatically generate a move ctor and move assignment operator yet, which means you need to define them yourself.

Next, when you return local variables, the compiler first tries to move them before actually going the usual route of copying them. However, to do that, it needs a move ctor. Since it doesn't have that, it tries the usual copy and through the generated copy ctor automatically invokes the copy constructor of std::list which in turn tries to invoke the copy ctor of its element type, which is private in std::unique_ptrs case.

You need to either define an appropriate move ctor or a copy ctor that doesn't invoke std::unique_ptr's copy ctor (i.e., make a deep copy of the content).

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • I don't know what are move ctor and move assignment operator and what they are for. Can you explain me? – Nick Jul 03 '12 at 20:26
  • 1
    I don't understand the last paragraph of your answer. `return std::move(local_variable);` is incorrect in all cases of which I am aware: it suppresses NRVO and the compiler should prefer move operations over copy operations when they are available (Visual C++ will correctly select the move constructor in this case). – James McNellis Jul 03 '12 at 20:27
  • @James: Right-y, that last paragraph somehow survived my edit. Fixed. – Xeo Jul 03 '12 at 20:36
2

Short answer: (C++11 specific) Items in a list must be copyable or movveable. A unique_ptr is not copyable by-design, but it is moveable, so long as the controlled type is also moveable.

Your type, data is not moveable because you have not implemented move semantics and the compiler did not do it for you.

Implement move semantics, and you can use unique_ptr in a list:

data(ddata&&) {};

According to thhe Standard, a move constructor would be generated for your class by the compiler. However, VS10 does not support this -- this might be the problem your'e running in to.

For further reference, see my post on CR: Canonical Implementation of Move Semantics

Community
  • 1
  • 1
John Dibling
  • 99,718
  • 31
  • 186
  • 324