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基本クラスが抽象であることに注意してください。以下に、説明のためにいくつかのコードを示します。

さらに、最近Webで、を使用して代入演算子をオーバーロードする方法の例を見つけましたstd::move。私は通常、コピースワップのイディオムに従います。上記の演算子をオーバーロードするためのこれらの2つの方法のどちらが、私の場合に意味がありますか?以下に、関数の定義も示します。

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

unique_ptr を使用した標準コンテナーの代わりに、Boost のポインター コンテナーを使用します。これらは、この種の使用のために設計されています。

于 2011-12-12T20:57:12.523 に答える
1

代わりに、ここでshared_ptrを使用する必要があると思います。

共有リソースを所有できるのは1つのunique_ptrのみです。意図したことを実行できる場合、つまり、unique_ptrを値で返すことができる場合は、ベクトル内の1つが破棄されます。これは、おそらく望ましくないことです。

于 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];
}

これで、const を逆参照して、必要なメンバー(const または non-const)unique_ptrを呼び出すことができます。Shape誤って をコピーしようとするunique_ptrと (所有権が から転送されFooます)、コンパイル時エラーが発生します。

unique_ptrこのソリューションは、からの偶発的な所有権の移転をキャッチするため、 への非 const 参照を返すよりも優れていFooます。Fooただし、 viaからの所有権の譲渡を許可する場合atは、非 const 参照の方が適切です。

Q2: さらに、最近 Web で std::move を使用して代入演算子をオーバーロードする方法の例を見つけました。私は通常、Copy-Swap イディオムに従います。上記の演算子をオーバーロードする 2 つの方法のうち、私の場合はどれが理にかなっていますか?

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ます。ただし、この明示的なものは、暗黙的なものよりも約 2 倍高速です。FooSwappableFooswap

上記のアドバイスはすべて、 から最高のパフォーマンスを引き出すことを目的としていFooます。必要に応じて、移動の割り当てで Copy-Swap イディオムを使用できます。それは正しく、少し遅くなります。swapただし、移動代入と移動代入の呼び出しで無限再帰が発生しないように注意してくださいswap。:-) 確かに、その落とし穴は、swap割り当てをきれいに (そして最適に) 分離して移動するもう 1 つの理由です。

アップデート

がこのShapeように見えると仮定すると、単一のデータ メンバがあると仮定して、 の移動コンストラクタ、移動代入、コピー コンストラクタ、およびコピー代入演算子をコーディングする 1 つの方法を次に示します。FooFoo

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);
}

コンパイラがデフォルトの move メンバーをサポートしている場合、次の方法で同じことが実現できます。

    Foo(Foo&&) = default;
    Foo& operator=(Foo&&) = default;

移動コンストラクターと移動代入演算子の場合。

上記により、それぞれが常にShape1 つのスマート ポインター/ベクター/Foo のみによって所有されることが保証されます。複数Fooの が の所有権を共有したい場合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 に答える