繁体   English   中英

在C ++中定义谓词函数的正确方法

[英]Correct Way to Define a Predicate Function in C++

我正在尝试编写用于STL算法的谓词函数。 我看到它们是两种定义谓词的方法:

(1)使用如下简单的功能:

bool isEven(unsigned int i)   
{ return (i%2 == 0); }

std::find_if(itBegin, itEnd, isEven); 

(2)使用operator()函数如下:

class checker {  
public:  
  bool operator()(unsigned int i)  
  { return (i%2 == 0); }  
}; 

std::find_if(itBegin, itEnd, checker); 

我更多地使用第二种类型,因为我通常想创建一个谓词对象,其中包含一些成员并在算法中使用它们。 当我在checker中添加相同的isEven函数并将其用作谓词时,我收到一个错误:
3.给出错误的语法:

class checker { 
    public: 
       bool isEven(unsigned int i) 
       { return (i%2 == 0); }
}; 

checker c; 
std::find_if(itBegin, itEnd, c.isEven); 

调用c.isEven会在编译期间发出错误,指出对某些函数的未定义引用。 有人可以解释为什么3.给出错误? 此外,我将不胜感激任何关于谓词和迭代器基础知识的指针。

指向成员函数的指针需要调用实例,并且您只将成员函数指针传递给std::find_if (实际上您的语法不正确,因此根本不起作用;正确的语法是std::find_if(itBegin, itEnd, &checker::isEven)然后由于我给出的原因仍然不起作用)。

find_if函数希望能够使用单个参数(要测试的对象)调用该函数,但它实际上需要两个来调用成员函数:实例this指针和要比较的对象。

重载operator()允许您同时传递实例和函数对象,因为它们现在是同一个东西。 使用成员函数指针,您必须将两条信息传递给只需要一个的函数。

有一种方法可以使用std::bind (需要<functional>头文件)来完成此操作:

checker c;
std::find_if(itBegin, itEnd, std::bind(&checker::isEven, &c, std::placeholders::_1));

如果你的编译器不支持std::bind ,你也可以使用boost::bind 虽然只是重载operator()没有真正的优势。


为了详细说明, std::find_if需要一个与签名bool (*pred)(unsigned int)匹配的函数指针或者那种行为方式。 它实际上不需要是函数指针,因为谓词的类型由模板绑定。 任何行为类似于bool (*pred)(unsigned int)都是可以接受的,这就是仿函数的工作原理:可以使用单个参数调用它们并返回bool

正如其他人所指出的那样, checker::isEven的类型是bool (checker::*pred)(unsigned int) ,它的行为与原始函数指针不同,因为它需要调用checker的实例。

指向成员函数的指针可以在概念上被认为是一个常规函数指针,它接受一个额外的参数,即this指针(例如bool (*pred)(checker*, unsigned int) )。 您实际上可以使用std::mem_fn(&checker::isEven) (也来自<functional> )生成一个可以这种方式调用的包装器。 这仍然没有帮助你,因为现在你有一个必须用两个参数而不是只有一个参数调用的函数对象, std::find_if仍然不喜欢。

使用std::bind将指向成员函数的指针视为this指针作为其第一个参数的函数。 传递给std::bind的参数指定第一个参数应始终为&c ,第二个参数应绑定到新返回的函数对象的第一个参数。 此函数对象是一个可以使用一个参数调用的包装器,因此可以与std::find_if一起使用。

虽然未指定std::bind的返回类型,但如果需要显式引用绑定的函数对象而不是传递它,则可以将其转换为std::function<bool(unsigned int)> (在此特定情况下)像我在我的例子中所做的那样直接进入另一个功能

checker::isEven不是一个函数; 它是一个成员函数。 如果没有对checker对象的引用,则无法调用非静态成员函数。 因此,您不能在任何可以传递函数指针的旧位置使用成员函数。 成员指针具有特殊语法,需要的不仅仅是()来调用。

这就是仿函数使用operator()的原因; 这使得对象可以调用,而不必使用成员函数指针。

我想这是因为c.isEven()的类型是,

bool (checker::*)(unsigned int) // member function of class

find_if()可能无法预料到这find_if() std::find_if应该是一个函数指针( bool (*)(unsigned int) )或函数对象。

编辑 :另一个约束: class对象必须调用static 成员函数指针 在您的情况下,即使您成功传递成员函数,然后find_if()也不会有任何有关任何checker对象的信息; 因此,为了接受成员函数指针参数而重载find_if()是没有意义的。

注意 :一般来说c.isEven不是传递成员函数指针的正确方法; 它应该传递为, &checker::isEven

我更喜欢仿函数 (函数对象),因为使程序更具可读性,更重要的是,清楚地表达意图。

这是我最喜欢的例子:

template <typename N>
struct multiplies
{
  N operator() (const N& x, const N& y) { return x * y; }
};

vector<int> nums{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Example accumulate with transparent operator functor 
double result = accumulate(cbegin(nums), cend(nums), 1.1, multiplies<>());

注意:近年来我们得到了lambda表达式支持。

// Same example with lambda expression
double result = accumulate(cbegin(nums), cend(nums), 1.1,
                            [](double x, double y) { return x * y; });

给出的示例说您应该使用调用运算符( operator() ),而在您的示例中,您调用了函数isEven 尝试将其重写为:

class checker { 
    public: 
       bool operator()(unsigned int i) 
       { return (i%2 == 0); }
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM