1

I have a class that has a const vector member that holds unique pointers to some objects. When constructed, the sequence object should steal the ownership of the vector of unique pointers that is passed to the constructor so that the sequence object is now the owner of the objects that were owned by the unique pointers in the vector parameter.

class sequence
{
    const std::vector< std::unique_ptr< statement > > m_statements;

    sequence( std::vector< std::unique_ptr< statement > > & statements );
};

The first time I tried to implement the constructor, I did the following :

sequence::sequence( vector< unique_ptr< statement > > & statements )
    m_statements( statements )
{
}

But of course this does not compile since one can't copy-construct a unique_ptr and thus can't copy-construct a vector.

C++ doesn't allow to initialize const members in the body of the constructor (like Java does with final members), but only within the initializer list. Thus, one possible solution could be to drop the const modifier of m_statement and, using a loop, move the content from one vector to the other in the body of the constructor.

But I want to keep this const modifier.

So I came up with another solution that seems to compile, but because I'm new to C++11, I'm not certain about what it does exacly. The idea was to embed the loop described above into a lambda function so that I could initialize m_statement within the initializer list using a loop and still keep the const modifier on m_statement.

sequence::sequence( vector< unique_ptr< const statement > > & statements ) :
    m_statements(([ & statements ] {
        vector< unique_ptr< const statement > > copied_vec;
        for( auto & stm : statements )
            copied_vec.push_back( move( stm ) );
        return copied_vec;
    })())
{
}

This compiles. But I'm not sure about what happens starting at the return statement of the lambda function.

I assume that a copy of copied_vec made and returned. What happens when you return by value a vector of unique pointers ? Is it a correct way to do what I want, despite being weird, or must I just drop the const modifier on m_statetent ? Thank you.

lorro
  • 10,687
  • 23
  • 36
Virus721
  • 8,061
  • 12
  • 67
  • 123
  • 1
    Doesn't answer the question on how your code works but [this](http://stackoverflow.com/questions/18282204/proper-way-of-transferring-ownership-of-a-stdvector-stdunique-ptr-int-t) shows you the right way to `move` the vector. – NathanOliver Jul 28 '16 at 11:40
  • You create a lambda, then you call it. Clever, albeit unnecessary. – lorro Jul 28 '16 at 11:41
  • @NathanOliver Thanks. I'm speechless to see how simple it was. – Virus721 Jul 28 '16 at 11:41

1 Answers1

6

Am I missing a reason why a move constructor can't be used?

sequence::sequence( vector< unique_ptr< statement > > && statements )
    m_statements( std::move(statements) )
{
}
KayEss
  • 2,290
  • 15
  • 31
  • Thanks for your help. I just didn't think about that. Though i'd still like to know what happens when i'm copying `copied_vec` using the return by value. – Virus721 Jul 28 '16 at 11:42
  • Depending on "details" I think you're likely to get some form of RVO. I think with C++17 it's even mandated (again, depending on details). – KayEss Jul 28 '16 at 11:45
  • Thanks. But ignoring that kind of implicit compiler optimization, what happens when you copy a vector of unique pointers ? The ownership is treansfered from the pointers of the source vector to the newly created vector's pointers ? – Virus721 Jul 28 '16 at 11:49
  • @Virus721: You cannot copy a vector of unique_ptrs. **Edit** [Demo](http://coliru.stacked-crooked.com/a/387367b1cd7f5af1) – AndyG Jul 28 '16 at 11:50
  • Not with a straight up copy, but your situation isn't quite a straight up copy. If you did use a real copy then you'd get a compiler error because `std::unique_ptr` has deleted the copy constructor and assignment operators. – KayEss Jul 28 '16 at 11:51
  • @KayEss So this only compiles because the compiler is able to get rid of a copy thanks to RVO ? If you disable any compiler optimization, isn't it going to compile anymore ? – Virus721 Jul 28 '16 at 11:52
  • RVO isn't dependant on optimisation level, and there are special rules for the return value of a function that allow the compiler to automatically invoke a move even when the code looks like it does a copy. – KayEss Jul 28 '16 at 11:53
  • @KayEss Also one more question please. Why is the `std::move` call necessary since the parameter is already a r-value reference ? Isn't `std::move` only a cast that generare a r-value references from something that isn't one ? – Virus721 Jul 28 '16 at 11:57
  • Good Q. I didn't run this through a compiler. It might not be necessary, but I put them in anyway to mark where the ownership moves. – KayEss Jul 28 '16 at 12:01
  • 2
    @Virus721 Once a object has a name it is a lvalue. Even though you got it as a rvalue reference it is a lvalue. – NathanOliver Jul 28 '16 at 12:03
  • @NathanOliver But in that case, isn't it up to the caller of the constructor to call `std::move` ? – Virus721 Jul 28 '16 at 12:18
  • 1
    @Virus721 Yes and no. The rvalue reference constructor will take a temporary or a vector that `move` has been applied to. Once you get to `m_statements( std::move(statements) )` `statements` is a named object so it a lvaue again. In order to move it into the member variable you need to call `move` again. – NathanOliver Jul 28 '16 at 12:22
  • @Virus721: That is one way for it to happen. Alternatively, if a function is simply returning a `vector>`, then that object is already an r-value, and this constructor will be chosen without any explicit `move` – AndyG Jul 28 '16 at 12:22