我有一个类Foo<T>
,它有一个智能指针向Shape
派生类的向量。
我正在尝试实现at(index)
成员函数。这就是我要直观地做的事情:
Foo<float> myfoo;
std::unique_ptr<Shape<float>> shape_ptr = myfoo.at(i);
shape_ptr->doSomething(param1, param2, ...);
定义at(index)
函数时,我收到编译器错误消息。请注意,定义了移动构造函数,并且Shape基类是抽象的。下面,我将提供一些代码用于说明目的。
此外,我最近在网上找到了一个关于如何使用std::move
重载赋值运算符的示例。我通常遵循Copy-Swap习语。重载上述操作符的两种方法中的哪一种对我的情况有意义?下面,我还将说明函数的定义。
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>'
}
答案 0 :(得分:2)
使用Boost's pointer containers代替带有unique_ptr的标准容器。它们是为这种用途而设计的。
答案 1 :(得分:1)
我认为你应该在这里使用 shared_ptr 。
只有一个 unique_ptr 可以拥有共享资源。如果你能够做你想做的事情,即按值返回unique_ptr ,那么向量中的那个将被销毁,这可能是你不想要的。
答案 2 :(得分:1)
Q1:如何处理Foo::at( int ) const
,您可以:
myfoo.at(i)->doSomething(param1, param2, ...);
未将所有权转移出vector<unique_ptr<Shape<T>>>
。
A1:Foo::at( int ) const
应返回const std::unique_ptr<Shape<T> >&
:
template < typename 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
),则会出现编译时错误。
此解决方案优于将非const引用返回unique_ptr
,因为它会从Foo
中捕获意外的所有权转移。但是,如果您希望通过Foo
允许从at
转出所有权,那么非const引用会更合适。
Q2:此外,我最近在网上找到了一个关于如何使用std :: move重载赋值运算符的示例。我通常遵循Copy-Swap习语。重载上述运算符的两种方法中的哪一种对我的情况有意义?
A2:我不确定~Foo()
是做什么的。如果它没有做任何事情,你可以删除它,然后(假设完全符合C ++ 11)你将自动获得正确和最佳的移动构造函数和移动赋值运算符(以及正确删除的复制语义)。
如果您无法删除~Foo()
(因为它确实有重要意义),或者您的编译器尚未实现自动移动生成,您可以明确地提供它们,就像您在问题中所做的那样。
你的移动构造函数是现场:移动构造成员。
您的移动分配应该类似(并且如果隐含~Foo()
则会自动生成):移动分配成员:
template < typename T >
Foo<T> & Foo<T>::operator =( Foo<T> && bank )
{
m_Bank = std::move(bank.m_Bank);
return (*this);
}
您的Foo
设计也适用于Swappable
,这总是很有用:
friend void swap(Foo& x, Foo& y) {x.m_Bank.swap(y.m_Bank);}
如果没有这个明确的swap
,您的Foo
仍然Swappable
使用Foo
的移动构造函数并移动分配。然而,这个显式的swap
大约是隐式的Foo
的两倍。
上述建议旨在获得swap
以外的最高性能。如果需要,您可以在移动作业中使用复制交换习语。这将是正确的,稍慢。虽然如果你确实小心,你没有通过swap
调用移动赋值和移动赋值调用swap
来获得无限递归! :-)确实,这个问题只是干净(并且最佳地)分离Shape
并移动任务的另一个原因。
<强>更新强>
假设Foo
看起来像this,这里有一种方法可以为Foo
代码移动构造函数,移动赋值,复制构造函数和复制赋值运算符,假设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);
}
单个数据成员:
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
...
Shape
如果您的编译器支持默认移动成员,则可以通过以下方式实现相同的目的:
Foo
用于移动构造函数和移动赋值运算符。
以上确保每个Shape
始终只由一个智能指针/向量/ Foo拥有。如果您希望多个std::vector< std::shared_ptr< Shape > > m_Bank;
共享{{1}} s的所有权,那么您可以拥有数据成员:
{{1}}
您可以默认所有移动构造函数,移动赋值,复制构造函数和复制赋值。
答案 3 :(得分:0)
好像你应该在这里返回一个引用:
Shape<T> & Foo<T>::at( int index ) const{
return *m_Bank[index];
}