C++中 predicate 函数的正确定义方法

gg0vcinb  于 2023-02-06  发布在  其他
关注(0)|答案(5)|浏览(310)

我正在尝试编写用于STL算法的 predicate 函数。我发现有两种定义 predicate 的方法:
(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);

我更喜欢使用第二种类型,因为我通常会创建一个 predicate 对象,其中包含一些成员,并在算法中使用它们。当我在checker中添加相同的isEven函数并将其用作 predicate 时,我得到一个错误:
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.会出现错误吗?另外,我希望能有任何指针来阅读 predicate 和迭代器的基础知识。

ovfsdjhp

ovfsdjhp1#

指向成员函数的指针需要一个示例来调用,而您只传递了指向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)匹配的函数指针,或者类似的函数指针,但实际上它不需要是函数指针,因为 predicate 的类型由模板绑定,任何类似bool (*pred)(unsigned int)的函数指针都是可以接受的,这就是函子的作用:可以使用单个参数调用它们并返回bool
正如其他人所指出的,checker::isEven的类型是bool (checker::*pred)(unsigned int),它的行为不像原始的函数指针,因为它需要一个checker的示例来调用。
指向成员函数的指针在概念上可以被视为接受附加参数this指针的常规函数指针(例如,bool (*pred)(checker*, unsigned int))。实际上,您可以使用std::mem_fn(&checker::isEven)生成一个可以通过这种方式调用的 Package 器(同样来自<functional>)。这仍然没有帮助,因为现在你有一个函数对象,必须用两个参数而不是只有一个参数来调用,std::find_if仍然不喜欢这样。
使用std::bind会将指向成员函数的指针视为以this指针作为第一个参数的函数。传递给std::bind的参数指定第一个参数应始终为&c。并且第二个参数应当绑定到新返回的函数对象的第一个参数。这个函数对象是一个 Package 器,可以用一个参数调用,因此可以与std::find_if一起使用。
尽管std::bind的返回类型是未指定的,但是如果需要显式引用绑定函数对象,而不是像我在示例中所做的那样将其直接传递给另一个函数,则可以将其转换为std::function<bool(unsigned int)>(在本例中)。

ht4b089n

ht4b089n2#

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

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

这可能不是X1 M1 N1 X所期望的。X1 M2 N1 X应当期望函数指针(X1 M3 N1 X)或函数对象。

编辑:另一个约束:非static成员函数指针必须由class对象调用。在您的情况下,即使您成功传递了成员函数,find_if()仍然不会有任何关于checker对象的信息;所以重载find_if()来接受成员函数指针参数是没有意义的。
:一般来说c.isEven不是传递成员函数指针的正确方式;它应该作为&checker::isEven传递。

holgip5t

holgip5t3#

checker::isEven不是函数;它是一个成员函数,而且你不能调用一个非静态的成员函数,除非你引用了一个对象,所以你不能在任何你可以传递函数指针的地方,使用一个成员函数,成员指针有特殊的语法,需要调用的不仅仅是()
这就是函子使用operator()的原因;这使得对象可调用而不必使用成员函数指针。

camsedfj

camsedfj4#

我更喜欢functors(函数对象),因为它使你的程序更具可读性,更重要的是,它清楚地表达了你的意图。
这是我最喜欢的例子:

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 expression支持。

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

6rvt4ljy5#

上面的例子要求你使用调用运算符(operator()),而在你的例子中你调用了函数isEven,试着重写如下:

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

相关问题