2011-10-12 72 views
2

我想知道关于谓词函子包装的约定和最佳实践。例如,给定的一类,如:包装谓词函子

class Timer 
{ 
public: 
    Timer(const std::string& name, int interval); 
    bool nameIs(const std::string& name) const; 
private: 
    std::string name_; 
    int interval_; 
}; 

TimerVec类使用的是(在一种情况下):

class TimerVec 
{ 
public: 
    typedef std::vector<Timer>::iterator iterator;`` 
    <... ctors, etc ...> 
    iterator findByName(const std::string& name); 
private: 
    std::vector<Timer> timers_; 
}; 

并具有谓词函子像:

class TimerNameIs 
{ 
public: 
    TimerNameIs(const std::string& name) : name_(name) {} 
    bool operator()(const Timer& t) { return t.nameIs(name_); } 
private: 
    const std::string& name_; 
}; 

我可以想想一些地方可以放上函子代码,有些是:

  1. 紧接在定时器声明之后的头文件中
  2. 嵌套在定时器内(即,所以裁判成为Timer::TimerNameIs
  3. 内TimerVec(目前唯一的用户)
  4. 嵌套在提前为TimerVec::findByName实施(再次仅它使用的地方)的匿名命名空间

虽然任何这些会我相当喜欢#2,但这不是我见过的。有没有具体的理由支持特定的选择?

+2

如果它只被一个函数使用,我会暂时使用#4。事实上,你可能根本不需要函子(C++ 11 lambda等)。如果您稍后发现其他用途,则重构应该很简单。 - 如果我选择重构,我绝对不会使用#3或任何其他强制无关代码重新编译的选项。 – UncleBens

+0

@UncleBens在这种情况下,lambda显然是最好的解决方案。但+1,因为我应该想到它,但没有。 – rlduffy

回答

1

这是开放辩论。我更喜欢创建一个嵌套类。通过这种方式,仅用于处理特定类型对象的仿函数是该对象内的命名空间范围。

我也通常命名谓词match_xxx其中xxx是我匹配的参数。

机智:

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

...这正是如此使用:

std::find_if(v.begin(), v.end(), Timer::match_name("Flibbidy")); 

在这个代号为6个月后看的时候我喜欢这种方法,因为Timer::match_name("Flibbidy")语义是非常清楚的。

我也很小心从std::unary_function派生我的仿函数(虽然我上面的派生可能有参数颠倒)。

+0

我知道任何答案都是有争议的,但你的推理是合理的。并且从unary_function派生出的建议是点亮的。所有这一切,你证实了我的偏见:) – rlduffy

1

我个人在它自己的头文件和cpp文件中。在TimerNameIs头文件使用#include "Timer.h"

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

这样做,你从一个到另一个隔离定时器和TimerNameIs。

+0

+1因为我也应该考虑这一点,但没有。该项目的风格是相关类被分组为源文件。另外,我不是Java风格的每类源文件模型的忠实粉丝。事情的实质是Timer和TimerNameI是无情的耦合,所以为什么不让源代码组确认? – rlduffy

+0

@rlduffy所以,使用第二个选项。这样'TimeVec'可以毫无问题地使用它。考虑未来的发展和用途,不要使用第三种选择。 –