1

我有一个类,它有一个指向派生类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>'
}
4

4 回答 4

2

使用Boost 的指针容器代替具有 unique_ptr 的标准容器。它们专为这种用途而设计。

于 2011-12-12T20:57:12.523 回答
1

我认为你应该在这里使用shared_ptr

只有一个unique_ptr可以拥有共享资源。如果你能够做你想做的事,即按值返回一个 unique_ptr,那么向量中的那个将被破坏,这可能是你不想要的。

于 2011-12-12T19:51:26.123 回答
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];
}

现在您可以取消引用 constunique_ptr并调用他们想要的任何成员Shape(const 或 non-const)。如果他们不小心尝试复制unique_ptr, (这会将所有权转移出Foo),他们将收到编译时错误。

此解决方案比返回非常量引用要好,unique_ptr因为它会捕获意外的所有权转移出Foo. 但是,如果您想允许所有权转移出Foovia at,那么非常量引用会更合适。

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。如果需要,您可以在移动作业中使用 Copy-Swap 习语。这将是正确的,并且会稍微慢一些。虽然如果你小心,你不会通过swap调用移动分配和移动分配调用获得无限递归swap!:-) 事实上,这个问题只是干净(和最佳)分离swap和​​移动分配的另一个原因。

更新

假设Shape看起来像这样,这里有一种编写移动构造函数、移动赋值、复制构造函数和复制赋值运算符的方法Foo,假设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拥有。如果您希望多个Foos 共享 s 的所有权Shape,那么您可以将其作为您的数据成员:

std::vector< std::shared_ptr< Shape > > m_Bank;

您可以默认所有移动构造函数、移动赋值、复制构造函数和复制赋值。

于 2011-12-13T13:54:19.357 回答
0

看起来你应该只是在这里返回一个参考:

Shape<T> & Foo<T>::at( int index ) const{
    return *m_Bank[index];
}
于 2011-12-12T20:11:16.490 回答