简体   繁体   English

将STL算法与shared_ptr,函数对象一起使用

[英]Using STL algorithms with shared_ptr, function objects

I have a set of shared_ptr, and I'd like to use remove_copy_if with a custom function object for the predicate. 我有一组shared_ptr,我想将remove_copy_if与谓词的自定义函数对象一起使用。 I didn't know the "best" way to do it. 我不知道“最好”的方式。 Right now, I've got this working: 现在,我有这个工作:

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

I decided to do const references to shared_ptr because the object won't hold on to the pointer and this just seemed more efficient than an extra copy of the shared_ptr. 我决定对shared_ptr执行const引用,因为该对象不会保留指针,这似乎比shared_ptr的额外副本更有效。

It seems like it would be better to just take const references to the objects, but I couldn't get it to compile. 看起来对对象进行const引用似乎更好,但是我无法编译它。 I changed it to this, but no luck: 我把它改成了这个,但没有运气:

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

Here is the output from g++: 这是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

Is there another way to do it, or any suggestions? 有其他方法可以做到这一点,还是有任何建议?

First of all, since you're using the C++0x features ( std::shared_ptr ), it makes sense to use std::copy_if() to avoid having to call std::not1 . 首先,由于您使用的是C ++ 0x特性( std::shared_ptr ),因此使用std::copy_if()来避免必须调用std::not1是有意义的。

The first functor you wrote works, and a minimal compilable example would be something like this: https://ideone.com/XhuNu 您编写的第一个仿函数,以及一个最小的可编译示例将是这样的: https//ideone.com/XhuNu

The second functor does not work, as the compiler points out, due to mismatch between its argument_type (which is const Cell ) and the argument that it is being called with, which is const std::shared_ptr<Cell>& . 正如编译器所指出的那样,第二个仿函数不起作用,因为它的argument_type(它是const Cell )和它被调用的参数不匹配,这是const std::shared_ptr<Cell>&

It's simply not what the container contains! 它根本不是容器包含的内容! For all it knows at this point, those Cell objects may not even be copyable. 就其所知,此时,那些Cell对象甚至可能无法复制。

The second functor would indeed be a better thing to use if the container is a set of Cells, not a set of shared pointers to Cells. 如果容器是一组Cells,而不是一组指向Cells的共享指针,那么第二个仿函数确实是更好的选择。 It is considered good design to avoid shared ownership of objects anyway. 无论如何,它被认为是避免共享对象的好设计。

Example code that would compile with the second functor 使用第二个仿函数编译的示例代码

#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';
}

test run: https://ideone.com/kLiFn 测试运行: https//ideone.com/kLiFn

You could use boost::make_indirect_iterator to make std::remove_copy_if work on the Cell s instead of the shared_ptr s. 您可以使用boost::make_indirect_iterator使std::remove_copy_ifCell而不是shared_ptr However, since the algorithm would be working on the Cell s directly, the output iterator would also have to take Cell s and not shared_ptr s. 但是,由于算法将直接在Cell ,因此输出迭代器也必须采用Cell而不是shared_ptr Which means the output collection would have to be a collection of Cell s. 这意味着输出集合必须是Cell的集合。

If you want to store shared_ptr s, you'd have to transform the predicate somehow. 如果要存储shared_ptr ,则必须以某种方式转换谓词。 You can use boost::lambda to do just that. 你可以使用boost::lambda来做到这一点。

Cubbi's example modified to use 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's C++0x compiler doesn't know it's boost, so I changed std::shared_ptr to boost::shared_ptr, but that should make no difference) (ideone的C ++ 0x编译器不知道它的提升,因此我将std :: shared_ptr更改为boost :: shared_ptr,但这应该没有区别)

http://www.ideone.com/mtMUj http://www.ideone.com/mtMUj

ps: PS:

I decided to do const references to shared_ptr because the object won't hold on to the pointer and this just seemed more efficient than an extra copy of the shared_ptr. 我决定对shared_ptr执行const引用,因为该对象不会保留指针,这似乎比shared_ptr的额外副本更有效。

Yes, you should (almost) always pass shared_ptr s as reference-to-const, it makes a huge difference. 是的,您应该(几乎) 总是shared_ptr作为引用传递给const,它会产生巨大的差异。 Copying a shared_ptr on a platform with threads means at least one atomic instruction (CAS, atomic-increment or something similar), and those can be rather expensive. 在具有线程的平台上复制shared_ptr意味着至少有一条原子指令(CAS,原子增量或类似的东西),而这些指令可能相当昂贵。 (And of course destroying the copy will be equally expensive) (当然,销毁副本也同样贵)

The only exception I can think of would be if the function will copy the shared_ptr . 我能想到的唯一例外是函数是否会复制shared_ptr In that case you could either take it by value and use swap() to "copy" it, or provide an rvalue-reference overload. 在这种情况下,您可以按值获取它并使用swap()来“复制”它,或者提供rvalue-reference重载。 (If the function doesn't always copy the shared_ptr , the rvalue-reference overload would be the preferred solution). (如果函数并不总是复制shared_ptr ,则rvalue-reference重载将是首选解决方案)。

Of course it doesn't make a big difference if the function is expensive anyway, but if it's a very cheap function that might get inlined and is called in a thight loop, the difference can be quite noticeable. 当然,如果函数价格昂贵,它并没有太大的区别,但是如果它是一个非常便宜的函数,可能会被内联并在一个thight循环中被调用,那么差异就会非常明显。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM