I’ve a class Foo<T> which has a vector of smart pointers to Shape derived classes.
I’m trying to implement an at(index) member function. Here’s what I would to do intuitively:
Foo<float> myfoo;
std::unique_ptr<Shape<float>> shape_ptr = myfoo.at(i);
shape_ptr->doSomething(param1, param2, ...);
When defining the at(index) function, I’m getting a compiler error message. Note that the move constructor was defined and that the Shape base class is abstract. Below, I’m giving some code for illustration purposes.
Furthermore, I found recently on the web an example on how to overload the assignment operator using std::move. I usually follow the Copy-Swap idiom. Which of those two ways for overloading the mentioned operator makes sense for my case? Below, I’m also illustrating the function’s definition.
template < typename T >
class Foo{
public:
Foo();
Foo( Foo && );
~Foo();
void swap(Foo<T> &);
//Foo<T> & operator =( Foo<T> );
Foo<T> & operator =( Foo<T> && );
std::unique_ptr<Shape<T> > at ( int ) const; // error here!
int size() const;
private:
std::vector< std::unique_ptr<Shape<T> > > m_Bank;
};
template < typename T >
Foo<T>::Foo( Foo && other)
:m_Bank(std::move(other.m_Bank))
{
}
/*template < typename T >
void Filterbank<T>::swap(Filterbank<T> & refBank ){
using std::swap;
swap(m_Bank, refBank.m_Bank);
}
template < typename T >
Foo<T> & Filterbank<T>::operator =( Foo<T> bank ){
bank.swap(*this);
return (*this);
}*/
template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank ){
//bank.swap(*this);
m_Bank = std::move(bank.m_Bank);
return (*this);
}
template < typename T >
std::unique_ptr<Shape<T> > Foo<T>::at( int index ) const{
return m_Bank[index]; // Error here! => error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
}
Q1: What to do with
Foo::at( int ) constsuch that you can:without transferring ownership out of the
vector<unique_ptr<Shape<T>>>.A1:
Foo::at( int ) constshould return aconst std::unique_ptr<Shape<T> >&:Now your can dereference the const
unique_ptrand call any member ofShapethey want (const or non-const). If they accidentally try to copy theunique_ptr, (which would transfer ownership out ofFoo) they will get a compile time error.This solution is better than returning a non-const reference to
unique_ptras it catches accidental ownership transfers out ofFoo. However if you want to allow ownership transfers out ofFooviaat, then a non-const reference would be more appropriate.Q2: Furthermore, I found recently on the web an example on how to overload the assignment operator using std::move. I usually follow the Copy-Swap idiom. Which of those two ways for overloading the mentioned operator makes sense for my case?
A2: I’m not sure what
~Foo()does. If it doesn’t do anything, you could remove it, and then (assuming fully conforming C++11) you would automatically get correct and optimal move constructor and move assignment operator (and the proper deleted copy semantics).If you can’t remove
~Foo()(because it does something important), or if your compiler does not yet implement automatic move generation, you can supply them explicitly, as you have done in your question.Your move constructor is spot on: Move construct the member.
Your move assignment should be similar (and is what would be automatically generated if
~Foo()is implicit): Move assign the member:Your
Foodesign lends itself to beingSwappabletoo, and that is always good to supply:Without this explicit
swap, yourFoois stillSwappableusingFoo‘s move constructor and move assignment. However this explicitswapis roughly twice as fast as the implicit one.The above advice is all aimed at getting the very highest performance out of
Foo. You can use the Copy-Swap idiom in your move assignment if you want. It will be correct and slightly slower. Though if you do be careful that you don’t get infinite recursion withswapcalling move assignment and move assignment callingswap! 🙂 Indeed, that gotcha is just another reason to cleanly (and optimally) separateswapand move assignment.Update
Assuming
Shapelooks like this, here is one way to code the move constructor, move assignment, copy constructor and copy assignment operators forFoo, assumingFoohas a single data member:…
If your compiler supports defaulted move members, the same thing could be achieved with:
for the move constructor and move assignment operator.
The above ensures that at all times each
Shapeis owned by only one smart pointer/vector/Foo. If you would rather that multipleFoos share ownership ofShapes, then you can have as your data member:And you can default all of move constructor, move assignment, copy constructor and copy assignment.