【问题标题】:Packaging Predicate Functors包装谓词函子
【发布时间】:2011-10-13 05:07:41
【问题描述】:

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

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. 在紧跟 Timer 声明的头文件中
  2. 嵌套在 Timer 内(即,引用变为 Timer::TimerNameIs
  3. 嵌套在 TimerVec 中(当前唯一的用户)
  4. 在实现 TimerVec::findByName 之前的匿名命名空间中(同样是唯一使用它的地方)

虽然其中任何一个都足够了,但我更喜欢 #2,但这不是我见过的。是否有任何具体的理由支持特定选项?

【问题讨论】:

  • 如果它只被一个函数使用,我暂时使用#4。事实上,您可能根本不需要仿函数(C++11 lambda 等)。如果以后发现其他用途,重构应该很简单。 - 如果我选择重构,我绝对不会使用 #3 或任何其他会强制重新编译无关代码的选项。
  • @UncleBens lambda 显然是最好的解决方案,如果它在这种情况下可用。但是 +1,因为我应该想到它,但没有。

标签: c++ predicate functor


【解决方案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") );

我更喜欢这种方法,因为Timer::match_name("Flibbidy") 的语义在 6 个月后查看这段代码时非常清晰。

我也很小心地从std::unary_function 派生出我的函子(尽管我上面的派生可能会颠倒参数)。

【讨论】:

  • 我知道任何答案都值得商榷,但你的推理是合理的。从 unary_function 派生的建议是准确的。所有这一切,你证实了我的偏见:)
【解决方案2】:

我个人,在它自己的头文件和 cpp 文件中。在TimerNameIsheader 文件中使用#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_;
};

这样做,您将 Timer 和 TimerNameIs 从一个隔离到另一个。

【讨论】:

  • +1 因为我也应该考虑到这一点,但没有。该项目的风格是将相关的类分组到源文件中。此外,我不是 Java 风格的“每个源文件一个类”模型的忠实拥护者。事实是 Timer 和 TimerNameIs 是不可分割的耦合,那么为什么不让源分组承认这一点呢?
  • @rlduffy 所以,使用第二个选项。这样TimeVec 可以毫无问题地使用它。考虑未来的发展和使用,不要使用第三种选择。
猜你喜欢
  • 2012-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多