shared_ptr、関数オブジェクトでのSTLアルゴリズムの使用

c++ function-object shared-ptr stl-algorithm
shared_ptr、関数オブジェクトでのSTLアルゴリズムの使用

shared_ptrのセットがあり、remove_copy_ifを述部のカスタム関数オブジェクトとともに使用したいと思います。 私はそれを行うための「最良の」方法を知りませんでした。 今、私はこれを機能させています:

class CellInCol : public std::unary_function,
                                             bool>
{
public:
    CellInCol( size_t col ) : _col( col ) {}
    bool operator() ( const std::shared_ptr &a ) const
    {
        return ( a->GetX() == _col );
    }
private:
    size_t _col;
};


    typedef std::set, CellSorter> Container;
    Container _grid;
    // initialization omitted...


Puzzle::Container Puzzle::GetCol( size_t c )
{
    Cell::Validate( c, 1, 9 );
    Container col;
    std::remove_copy_if( _grid.begin(), _grid.end(),
                         std::inserter( col, col.begin() ),
                         std::not1( CellInCol( c ) ) );
    return col;
}

オブジェクトがポインターを保持しないため、shared_ptrへのconst参照を行うことにしました。これは、shared_ptrの余分なコピーよりも効率的であるように見えたためです。

オブジェクトへのconst参照を取得する方が良いようですが、コンパイルすることができませんでした。 これに変更しましたが、運はありません:

class CellInCol : public std::unary_function
{
public:
    CellInCol( size_t col ) : _col( col ) {}

    // note use of const ref to shared_ptr's
    bool operator() ( const Cell &a ) const
    {
        return ( a.GetX() == _col );
    }
private:
    size_t _col;
};

g ++からの出力は次のとおりです。

In file included from /usr/include/c++/4.4/algorithm:62,
                 from /usr/include/c++/4.4/valarray:41,
                 from Puzzle.h:5,
                 from Puzzle.cpp:2:
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘_OIter std::remove_copy_if(_IIter, _IIter, _OIter, _Predicate) [with _IIter = std::_Rb_tree_const_iterator >, _OIter = std::insert_iterator, Sudoku::CellSorter, std::allocator > > >, _Predicate = std::unary_negate::CellInRow>]’:
Puzzle.cpp:100:   instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:938: error: no match for call to ‘(std::unary_negate::CellInRow>) (const std::shared_ptr&)’
/usr/include/c++/4.4/bits/stl_function.h:357: note: candidates are: bool std::unary_negate<_Predicate>::operator()(const typename _Predicate::argument_type&) const [with _Predicate = Sudoku::::CellInRow]
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘_OIter std::remove_copy_if(_IIter, _IIter, _OIter, _Predicate) [with _IIter = std::_Rb_tree_const_iterator >, _OIter = std::insert_iterator, Sudoku::CellSorter, std::allocator > > >, _Predicate = std::unary_negate::CellInCol>]’:
Puzzle.cpp:110:   instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:938: error: no match for call to ‘(std::unary_negate::CellInCol>) (const std::shared_ptr&)’
/usr/include/c++/4.4/bits/stl_function.h:357: note: candidates are: bool std::unary_negate<_Predicate>::operator()(const typename _Predicate::argument_type&) const [with _Predicate = Sudoku::::CellInCol]
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘_OIter std::remove_copy_if(_IIter, _IIter, _OIter, _Predicate) [with _IIter = std::_Rb_tree_const_iterator >, _OIter = std::insert_iterator, Sudoku::CellSorter, std::allocator > > >, _Predicate = std::unary_negate::CellInBlock>]’:
Puzzle.cpp:121:   instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:938: error: no match for call to ‘(std::unary_negate::CellInBlock>) (const std::shared_ptr&)’
/usr/include/c++/4.4/bits/stl_function.h:357: note: candidates are: bool std::unary_negate<_Predicate>::operator()(const typename _Predicate::argument_type&) const [with _Predicate = Sudoku::::CellInBlock]
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘_OIter std::remove_copy_if(_IIter, _IIter, _OIter, _Predicate) [with _IIter = std::_Rb_tree_const_iterator >, _OIter = std::insert_iterator, Sudoku::CellSorter, std::allocator > > >, _Predicate = std::unary_negate::CellIsNeighbor>]’:
Puzzle.cpp:154:   instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:938: error: no match for call to ‘(std::unary_negate::CellIsNeighbor>) (const std::shared_ptr&)’
/usr/include/c++/4.4/bits/stl_function.h:357: note: candidates are: bool std::unary_negate<_Predicate>::operator()(const typename _Predicate::argument_type&) const [with _Predicate = Sudoku::::CellIsNeighbor]
make: *** [Puzzle.o] Error 1

それを行う別の方法、または提案はありますか?

  6  1


ベストアンサー

まず、C ++ 0xの機能( `std

shared_ptr`)を使用しているため、` std :: copy_if() を使用して std :: not1`を呼び出さなくて済むようにします。

最初に作成したファンクターは機能し、最小限のコンパイル可能な例は次のようになります。https://ideone.com/XhuNu

コンパイラが指摘するように、2番目のファンクターは、argument_type( const Cell)と呼び出される引数(const std

shared_ptr&`)の不一致のために機能しません。

コンテナに含まれるものではありません! この時点で知っているすべての場合、それらのCellオブジェクトはコピー可能でさえない場合があります。

コンテナーがセルへの共有ポインターのセットではなく、セルのセットである場合、2番目のファンクターは実際に使用するのに適しています。 とにかくオブジェクトの共有所有権を避けることは良い設計と考えられています。

2番目のファンクターでコンパイルするサンプルコード

#include
#include
#include
#include

struct Cell {
        int mX;
        Cell(int x) : mX(x) {}
        size_t GetX() const { return mX;}
};
struct CellSorter {
        bool operator()(const Cell& l, const Cell& r) const
        {
                return l.GetX() < r.GetX();
        }
};

// your second functor begins
class CellInCol : public std::unary_function
{
public:
    CellInCol( size_t col ) : _col( col ) {}

    // note use of const ref to shared_ptr's
    bool operator() ( const Cell &a ) const
    {
        return ( a.GetX() == _col );
    }
private:
    size_t _col;
};
// your second functor ends

int main()
{
    typedef std::set Container;
    Container _grid = {Cell(1), Cell(2), Cell(7), Cell(10)};
    Container col;
    size_t c = 7;
    std::remove_copy_if( _grid.begin(), _grid.end(),
                         std::inserter( col, col.begin() ),
                         std::not1( CellInCol( c ) ) );
    std::cout << "col has " << col.size() << " elements\n"
              << "the first element is " << col.begin()->GetX() << '\n';
}

テスト実行:https://ideone.com/kLiFn

4


`boost

make_indirect_iterator`を使用して` std :: remove_copy_if`を shared_ptr`ではなく Cell`で動作させることができます。 ただし、アルゴリズムは「Cell」を直接処理するため、出力イテレータは「shared_ptr」ではなく「Cell」を取得する必要があります。 つまり、出力コレクションは `Cell`のコレクションでなければなりません。

`shared_ptr`を保存したい場合は、何らかの方法で述語を変換する必要があります。 `boost

lambda`を使用してそれを行うことができます。

`boost

lambda`を使用するように変更されたCubbiの例:

#include
#include
#include
#include
#include
#include
#include
#include


struct Cell {
        int mX;
        Cell(int x) : mX(x) {}
        size_t GetX() const { return mX;}
};
struct CellSorter {
        bool operator()(const boost::shared_ptr& l, const boost::shared_ptr& r) const
        {
                return l->GetX() < r->GetX();
        }
};

class CellInCol : public std::unary_function
{
public:
    CellInCol( size_t col ) : _col( col ) {}

    // note use of const ref to shared_ptr's
    bool operator() ( const Cell &a ) const
    {
        return ( a.GetX() == _col );
    }
private:
    size_t _col;
};

int main()
{
    typedef std::set, CellSorter> Container;
    Container _grid;
    _grid.insert( boost::shared_ptr(new Cell(1)));
    _grid.insert( boost::shared_ptr(new Cell(2)));
    _grid.insert( boost::shared_ptr(new Cell(7)));
    _grid.insert( boost::shared_ptr(new Cell(10)));
    Container col;
    size_t c = 7;

    std::remove_copy_if(
        _grid.begin(),
        _grid.end(),
        std::inserter( col, col.begin() ),
        !boost::lambda::bind(CellInCol(c), *boost::lambda::_1) // <------ :^)
    );

    std::cout << "col has " << col.size() << " elements\n"
              << " the first element is " << (*col.begin())->GetX() << '\n';
}
(ideoneのC ++ 0xコンパイラは、ブーストであることを知らないため、std

shared_ptrをboost :: shared_ptrに変更しましたが、違いはありません)

ps:

_
オブジェクトがポインターを保持しないため、shared_ptrへのconst参照を行うことにしました。これは、shared_ptrの余分なコピーよりも効率的であるように見えたためです。
_

はい、(ほとんど)_shared_ptrをconst参照として渡す必要があります。これは大きな違いをもたらします。 スレッドのあるプラットフォームで「shared_ptr」をコピーすることは、少なくとも1つのアトミック命令(CAS、アトミックインクリメント、または同様のもの)を意味し、それらはかなり高価になる可能性があります。 (そしてもちろん、コピーを破壊することも同様に費用がかかります)

私が考えることができる唯一の例外は、関数が `shared_ptr`をコピーする場合です。 その場合、値で取得して `swap()`を使用して「コピー」するか、右辺値参照のオーバーロードを提供できます。 (関数が `shared_ptr`を_always_コピーしない場合、右辺値参照のオーバーロードが推奨されるソリューションになります)。

もちろん、関数がとにかく高価である場合、大きな違いはありませんが、インライン化される可能性のある非常に安価な関数であり、8回のループで呼び出される場合、その違いは顕著になります。

2


タイトルとURLをコピーしました