在stl容器中处理智能指针 [英] Handling smart pointers in stl container
问题描述
我有一个类 Foo< T>
,它有一个智能指针的向量到 Shape
派生类。
我试图在(index)成员函数实现。这里是我将直观地做:
Foo< float& myfoo;
std :: unique_ptr< Shape< float>> shape_ptr = myfoo.at(i);
shape_ptr-> doSomething(param1,param2,...);
在(index)函数中定义 ,我得到一个编译器错误消息。注意,移动构造函数是定义的,Shape基类是抽象的。
此外,我最近在网上找到一个如何使用 std :: move
。我通常遵循复制交换习语。重载上述操作符的两种方法中的哪一种对我的情况有意义?下面,我还说明了函数的定义。
template<类型名T>
pre>
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; //错误这里!
int size()const;
private:
std :: vector< std :: unique_ptr< Shape< T> > > m_Bank;
};
template<类型名T>
Foo< T> :: Foo(Foo&&&其他)
:m_Bank(std :: move(other.m_Bank))
{
}
/ * template<类型名T>
void Filterbank< T> :: swap(Filterbank< T& refBank){
using std :: swap;
swap(m_Bank,refBank.m_Bank);
}
template<类型名T>
Foo< T> & Filterbank< T> :: operator =(Foo T bank){
bank.swap(* this);
return(* this);
} * /
template<类型名T>
Foo& Foo T :: operator =(Foo T&&&&&&bank){
//bank.swap(*this);
m_Bank = std :: move(bank.m_Bank);
return(* this);
}
template<类型名T>
std :: unique_ptr< Shape< T> > Foo T :: ::(int index)const {
return m_Bank [index]; //错误这里! =>错误C2248:'std :: unique_ptr< _Ty> :: unique_ptr':无法访问在'std :: unique_ptr< _Ty>'中声明的私有成员
}
解决方案Q1:如何处理
Foo :: at(int)const
,以便您可以:myfoo.at(i) - > doSomething(param1,param2,... );无需将所有权转移到
向量< unique_ptr< Shape< T>>之外的
;> 。
A1:
Foo :: at(int)const
return aconst std :: unique_ptr< Shape< T> &
:类型名T>
const std :: unique_ptr< Shape< T> >&
Foo< T> :: at(int index)const
{
return m_Bank [index];
}
现在你可以引用const
unique_ptr
并调用Shape
的任何成员(const或非const)。如果他们不小心试图复制unique_ptr
(它将转移Foo
的所有权),他们将获得一个编译时间错误。
此解决方案优于将非常量引用返回到
unique_ptr
,因为它捕获意外所有权转移的Foo
。但是,如果你想允许通过在
通过
Foo
进行所有权转移,那么非const引用将更合适。
Q2:此外,我最近在网上找到一个关于如何使用std :: move重载赋值运算符的示例。我通常遵循复制交换习语。
A2:我不知道什么
〜Foo()
。如果它不做任何事情,你可以删除它,然后(假设完全符合C ++ 11),你会自动得到正确和最佳的移动构造函数和移动赋值运算符(和正确的删除的副本语义)。
如果你不能删除
〜Foo()
(因为它有重要的作用),或者如果你的编译器还没有实现
您的移动构造函数是现在:移动构造成员。
你的移动赋值应该是类似的(并且是
〜Foo()
是隐式的时会自动生成的):Move assign the member :模板<类型名T>
Foo< T> & Foo T :: operator =(Foo T&&&&&&bank)
{
m_Bank = std :: move(bank.m_Bank);
return(* this);
}
您的
Foo
也适用于可插拔
,这总是很好的供应:friend void swap(Foo& x,Foo& y){x.m_Bank.swap(y.m_Bank);}
b $ b没有这个显式的
swap
,你的Foo
仍然code>使用
Foo
的移动构造函数和移动赋值。然而,这个显式的swap
的速度大约是隐式速度的两倍。
上述建议都是为了在
Foo
中性能最高。如果需要,您可以在移动分配中使用复制交换惯用法。这将是正确和稍慢。虽然如果你小心,你不会得到无限递归与swap
调用移动分配和移动分配调用swap
! :-)的确,这个秘密只是干净地(和最佳地)分离swap
并移动赋值的另一个原因。
更新
假设
Shape
看起来像这,这里是一种方法来代码移动构造函数,移动分配,复制构造函数和复制赋值运算符Foo
,假设Foo
有一个数据成员:std: :vector& std :: unique_ptr<形状> > m_Bank;
...
Foo :: Foo(Foo&&&&&&&& other)
:m_Bank(std :: move(other.m_Bank))
{
}
Foo :: Foo(const Foo& other)
{
for(const auto& p:other.m_Bank)
m_Bank.push_back(std :: unique_ptr< Shape> gt; clone():nullptr));
}
Foo&
Foo :: operator =(Foo&& other)
{
m_Bank = std :: move(other.m_Bank);
return(* this);
}
Foo&
Foo :: operator =(const Foo& other)
{
if(this!=& other)
{
m_Bank.clear
for(const auto& p:other.m_Bank)
m_Bank.push_back(std :: unique_ptr< Shape>(p?p-> clone():nullptr)
}
return(* this);
}
如果你的编译器支持默认的移动成员,
Foo(Foo&&)=
Foo& operator =(Foo&&)= default;
用于移动构造函数和移动赋值运算符。
以上确保每一个
Shape
只有一个智能指针/向量/ Foo。如果你想多个Foo
共享Shape
的所有权,那么你可以作为你的数据成员: / p>
std :: vector< std :: shared_ptr<形状> > m_Bank;
你可以默认所有的移动构造函数,移动赋值,拷贝构造函数和拷贝赋值。 >
I've a class
Foo<T>
which has a vector of smart pointers toShape
derived classes. I'm trying to implement anat(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 ) const
such that you can:myfoo.at(i)->doSomething(param1, param2, ...);
without transferring ownership out of the
vector<unique_ptr<Shape<T>>>
.A1:
Foo::at( int ) const
should return aconst std::unique_ptr<Shape<T> >&
:template < typename T > const std::unique_ptr<Shape<T> >& Foo<T>::at( int index ) const { return m_Bank[index]; }
Now your can dereference the const
unique_ptr
and call any member ofShape
they 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_ptr
as it catches accidental ownership transfers out ofFoo
. However if you want to allow ownership transfers out ofFoo
viaat
, 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:template < typename T > Foo<T> & Foo<T>::operator =( Foo<T> && bank ) { m_Bank = std::move(bank.m_Bank); return (*this); }
Your
Foo
design lends itself to beingSwappable
too, and that is always good to supply:friend void swap(Foo& x, Foo& y) {x.m_Bank.swap(y.m_Bank);}
Without this explicit
swap
, yourFoo
is stillSwappable
usingFoo
's move constructor and move assignment. However this explicitswap
is 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 withswap
calling move assignment and move assignment callingswap
! :-) Indeed, that gotcha is just another reason to cleanly (and optimally) separateswap
and move assignment.Update
Assuming
Shape
looks like this, here is one way to code the move constructor, move assignment, copy constructor and copy assignment operators forFoo
, assumingFoo
has a single data member:std::vector< std::unique_ptr< Shape > > m_Bank;
...
Foo::Foo(Foo&& other) : m_Bank(std::move(other.m_Bank)) { } Foo::Foo(const Foo& other) { for (const auto& p: other.m_Bank) m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr)); } Foo& Foo::operator=(Foo&& other) { m_Bank = std::move(other.m_Bank); return (*this); } Foo& Foo::operator=(const Foo& other) { if (this != &other) { m_Bank.clear(); for (const auto& p: other.m_Bank) m_Bank.push_back(std::unique_ptr< Shape >(p ? p->clone() : nullptr)); } return (*this); }
If your compiler supports defaulted move members, the same thing could be achieved with:
Foo(Foo&&) = default; Foo& operator=(Foo&&) = default;
for the move constructor and move assignment operator.
The above ensures that at all times each
Shape
is owned by only one smart pointer/vector/Foo. If you would rather that multipleFoo
s share ownership ofShape
s, then you can have as your data member:std::vector< std::shared_ptr< Shape > > m_Bank;
And you can default all of move constructor, move assignment, copy constructor and copy assignment.
这篇关于在stl容器中处理智能指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!