Packaging Predicate Functors
I'm wondering about conventions and best practices regarding the packaging of predicate functors. For example, given a class like:
class Timer
{
public:
Timer(const std::string& name, int interval);
bool nameIs(const std::string& name) const;
private:
std::string name_;
int interval_;
};
that is (in one case) used in class TimerVec
:
class TimerVec
{
public:
typedef std::vector<Timer>::iterator iterator;``
<... ctors, etc ...>
iterator findByName(const std::string& name);
private:
std::vector<Timer>开发者_开发技巧; timers_;
};
and has a predicate functor like:
class TimerNameIs
{
public:
TimerNameIs(const std::string& name) : name_(name) {}
bool operator()(const Timer& t) { return t.nameIs(name_); }
private:
const std::string& name_;
};
I can think of a number of places to put the functor code, some being:
- In the header file immediately following the declaration of Timer
- Nested inside Timer (i.e. so the ref becomes
Timer::TimerNameIs
) - Nested inside TimerVec (currently the only user)
- In an anonymous namespace ahead of the implementation for
TimerVec::findByName
(again the only place it's used)
While any of these would be adequate I'm rather drawn to #2, but it's not something I've ever seen done. Are there any concrete reasons favoring a particular option?
This is open to debate. I prefer to create a nested class. This way a functor that is intended only to work with a particular type of object is namespace-scoped within that object.
I also generally name the predicate match_xxx
where xxx
is the parameter I'm matching on.
To wit:
class Timer
{
// ...
public:
class match_name : public std::unary_function<Timer, bool>
{
public:
match_name(const std::string& name) : name_(name) {}
bool operator()(const Timer& t) { return t.nameIs(name_); }
private:
const std::string& name_;
};
};
...which is utilized thusly:
std::find_if( v.begin(), v.end(), Timer::match_name("Flibbidy") );
I prefer this method because the semantics of Timer::match_name("Flibbidy")
are exceedingly clear when looking at this code 6 months later.
I also am careful to derive my functor from std::unary_function
(although my derivation above might have the parameters reversed).
Me, personally, in it's own header and cpp files. Using #include "Timer.h"
in the TimerNameIs
header file:
#include "Timer.h"
#include <string>
class TimerNameIs
{
public:
TimerNameIs(const std::string& name) : name_(name) {}
bool operator()(const Timer& t) { return t.nameIs(name_); }
private:
const std::string& name_;
};
Doing this, you isolate Timer and TimerNameIs from one to the other.
精彩评论