6

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

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


    typedef std::set<std::shared_ptr<Cell>, 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<const Cell,
                                             bool>
{
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<std::shared_ptr<Sudoku::Cell> >, _OIter = std::insert_iterator<std::set<std::shared_ptr<Sudoku::Cell>, Sudoku::CellSorter, std::allocator<std::shared_ptr<Sudoku::Cell> > > >, _Predicate = std::unary_negate<Sudoku::<unnamed>::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<Sudoku::<unnamed>::CellInRow>) (const std::shared_ptr<Sudoku::Cell>&)’
/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::<unnamed>::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<std::shared_ptr<Sudoku::Cell> >, _OIter = std::insert_iterator<std::set<std::shared_ptr<Sudoku::Cell>, Sudoku::CellSorter, std::allocator<std::shared_ptr<Sudoku::Cell> > > >, _Predicate = std::unary_negate<Sudoku::<unnamed>::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<Sudoku::<unnamed>::CellInCol>) (const std::shared_ptr<Sudoku::Cell>&)’
/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::<unnamed>::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<std::shared_ptr<Sudoku::Cell> >, _OIter = std::insert_iterator<std::set<std::shared_ptr<Sudoku::Cell>, Sudoku::CellSorter, std::allocator<std::shared_ptr<Sudoku::Cell> > > >, _Predicate = std::unary_negate<Sudoku::<unnamed>::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<Sudoku::<unnamed>::CellInBlock>) (const std::shared_ptr<Sudoku::Cell>&)’
/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::<unnamed>::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<std::shared_ptr<Sudoku::Cell> >, _OIter = std::insert_iterator<std::set<std::shared_ptr<Sudoku::Cell>, Sudoku::CellSorter, std::allocator<std::shared_ptr<Sudoku::Cell> > > >, _Predicate = std::unary_negate<Sudoku::<unnamed>::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<Sudoku::<unnamed>::CellIsNeighbor>) (const std::shared_ptr<Sudoku::Cell>&)’
/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::<unnamed>::CellIsNeighbor]
make: *** [Puzzle.o] Error 1

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

4

2 に答える 2

4

まず第一に、C++0x の機能 ( ) を使用しているため、 を呼び出す必要がないようstd::shared_ptrに使用することは理にかなっています。std::copy_if()std::not1

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

コンパイラが指摘するように、2 番目のファンクターは、argument_type ( const Cell) と呼び出される引数 ( ) が一致しないため、機能しませんconst std::shared_ptr<Cell>&

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

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

2 番目のファンクターでコンパイルされるコード例

#include <set>
#include <functional>
#include <algorithm>
#include <iostream>

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<const Cell,
                                             bool>
{
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<Cell, CellSorter> 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

于 2011-03-25T20:46:22.053 に答える
2

の代わりに を使用boost::make_indirect_iteratorしてstd::remove_copy_if動作させることができます。ただし、アルゴリズムは s に直接作用するため、出力反復子もs ではなくsを取る必要があります。つまり、出力コレクションは のコレクションでなければなりません。Cellshared_ptrCellCellshared_ptrCell

sを格納したい場合はshared_ptr、何らかの方法で述語を変換する必要があります。boost::lambdaまさにそれを行うために使用できます。

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

#include <memory>
#include <set>
#include <functional>
#include <algorithm>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>


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

class CellInCol : public std::unary_function<Cell, bool>
{
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<boost::shared_ptr<Cell>, CellSorter> Container;
    Container _grid;
    _grid.insert( boost::shared_ptr<Cell>(new Cell(1)));
    _grid.insert( boost::shared_ptr<Cell>(new Cell(2)));
    _grid.insert( boost::shared_ptr<Cell>(new Cell(7)));
    _grid.insert( boost::shared_ptr<Cell>(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 に変更しましたが、違いはありません)

http://www.ideone.com/mtMUj

PS:

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

はい、(ほぼ)常にs を const への参照として渡す必要がありますshared_ptr。これにより、大きな違いが生じます。スレッドを使用してプラットフォームに a をコピーするshared_ptrことは、少なくとも 1 つのアトミック命令 (CAS、atomic-increment など) を意味し、それらはかなり高価になる可能性があります。(そしてもちろん、コピーを破棄することも同様にコストがかかります)

私が考えることができる唯一の例外は、関数がshared_ptr. その場合、値で取得しswap()て「コピー」するために使用するか、右辺値参照のオーバーロードを提供できます。(関数が常に をコピーするとは限らない場合はshared_ptr、右辺値参照のオーバーロードが推奨される解決策になります)。

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

于 2011-03-26T05:18:00.467 に答える