使用带有shared_ptr、函数对象的STL算法
我有一组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进行常量引用,因为该对象不会保留指针,而且这似乎比shared_ptr的额外副本更有效。
看起来最好只对对象进行常量引用,但我无法编译它。我将其更改为这样,但没有运气:
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
是否有其他方法可以做到这一点,或者有什么建议?
I have a set of shared_ptr, and I'd like to use remove_copy_if with a custom function object for the predicate. 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.
It seems like it would be better to just take const references to the objects, but I couldn't get it to compile. 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++:
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
首先,由于您使用的是 C++0x 功能 (
std::shared_ptr
),因此使用std::copy_if()
以避免调用std::not1
。您编写的第一个函子有效,最小的可编译示例如下所示: https://ideone.com/XhuNu
正如编译器所指出的,第二个函子不起作用,因为它的 argument_type(const Cell)与调用它的参数(const const)之间不匹配std::shared_ptr&。 |
这根本就不是容器所含的东西!据目前所知,这些 Cell 对象甚至可能不可复制。
如果容器是一组 Cell,而不是一组指向 Cell 的共享指针,那么使用第二个函子确实会更好。无论如何,避免对象的共享所有权被认为是良好的设计。
使用第二个函子测试运行进行编译的示例代码
:https://ideone.com/kLiFn
First of all, since you're using the C++0x features (
std::shared_ptr
), it makes sense to usestd::copy_if()
to avoid having to callstd::not1
.The first functor you wrote works, and a minimal compilable example would be something like this: 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 isconst 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.
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. It is considered good design to avoid shared ownership of objects anyway.
Example code that would compile with the second functor
test run: https://ideone.com/kLiFn
您可以使用
boost::make_indirect_iterator
使std::remove_copy_if
在Cell
上工作,而不是在shared_ptr
上工作s。但是,由于该算法将直接处理Cell
,因此输出迭代器也必须采用Cell
而不是shared_ptr
。这意味着输出集合必须是 Cell 的集合。如果您想存储
shared_ptr
,则必须以某种方式转换谓词。您可以使用 boost::lambda 来做到这一点。Cubbi 的示例修改为使用
boost::lambda
:(ideone 的 C++0x 编译器不知道它是 boost,所以我将 std::shared_ptr 更改为 boost::shared_ptr,但这应该没有什么区别)
http://www.ideone.com/mtMUj
ps:
是的,您应该(几乎)总是将
shared_ptr
作为const引用传递,这会产生巨大的差异。在具有线程的平台上复制shared_ptr
意味着至少一条原子指令(CAS、原子增量或类似指令),而这些指令可能相当昂贵。 (当然,销毁副本的代价同样昂贵)我能想到的唯一例外是该函数是否会复制
shared_ptr
。在这种情况下,您可以按值获取它并使用 swap() 来“复制”它,或者提供右值引用重载。 (如果函数不总是复制shared_ptr
,则右值引用重载将是首选解决方案)。当然,如果该函数无论如何都很昂贵,那并没有什么大的区别,但如果它是一个非常便宜的函数,可能会被内联并在大腿循环中调用,那么差异可能会非常明显。
You could use
boost::make_indirect_iterator
to makestd::remove_copy_if
work on theCell
s instead of theshared_ptr
s. However, since the algorithm would be working on theCell
s directly, the output iterator would also have to takeCell
s and notshared_ptr
s. Which means the output collection would have to be a collection ofCell
s.If you want to store
shared_ptr
s, you'd have to transform the predicate somehow. You can useboost::lambda
to do just that.Cubbi's example modified to use
boost::lambda
:(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)
http://www.ideone.com/mtMUj
ps:
Yes, you should (almost) always pass
shared_ptr
s as reference-to-const, it makes a huge difference. Copying ashared_ptr
on a platform with threads means at least one atomic instruction (CAS, atomic-increment or something similar), and those can be rather expensive. (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
. In that case you could either take it by value and useswap()
to "copy" it, or provide an rvalue-reference overload. (If the function doesn't always copy theshared_ptr
, the rvalue-reference overload would be the preferred solution).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.