[英]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_if
在Cell
而不是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.