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

时间:2021-04-05 19:11:42

I'm trying to write predicate function for use with STL algorithms. I see that they are two ways to define a predicate:

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

(1) Use a simple function as below:

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

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

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

(2) Use the operator() function as below:

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

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

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

I have more use for the second type as I usually would like to create a predicate object with some members in it and use them in the algorithm. When I add the same isEven function inside checker and use it as a predicate, I get an error:
3. Syntax which gives error:

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

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

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

Calling c.isEven gives an error during compilation saying undefined reference to some function. Can someone explain why 3. is giving error? Also, I would appreciate any pointers to read about predicate and iterator basics.

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

5 个解决方案

#1


4  

I guess it's because the type of c.isEven() is,

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

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

which may not be expected by find_if(). std::find_if should be expecting either a function pointer (bool (*)(unsigned int)) or a function object.

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

Edit: Another constraint: A non-static member function pointer must be called by the class object. In your case, even if you succeed to pass the member function then still find_if() will not have any information about any checker object; so it doesn't make sense to have find_if() overloaded for accepting a member function pointer argument.

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

Note: In general c.isEven is not the right way to pass member function pointer; it should be passed as, &checker::isEven.

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

#2


10  

A pointer to a member function requires an instance to be called on, and you are passing only the member function pointer to std::find_if (actually your syntax is incorrect, so it doesn't work at all; the correct syntax is std::find_if(itBegin, itEnd, &checker::isEven) which then still doesn't work for the reasons I gave).

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

The find_if function expects to be able to call the function using a single parameter (the object to test), but it actually needs two to call a member function: the instance this pointer and the object to compare.

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

Overloading operator() allows you to pass both the instance and the function object at the same time, because they're now the same thing. With a member function pointer you must pass two pieces of information to a function that expects only one.

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

There is a way to do this using std::bind (which requires the <functional> header):

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

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

If your compiler doesn't support std::bind, you can also use boost::bind for this. Though there's no real advantage to doing this over just overloading operator().

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


To elaborate a bit more, std::find_if expects a function pointer matching the signature bool (*pred)(unsigned int) or something that behaves that way. It doesn't actually need to be a function pointer, because the type of the predicate is bound by the template. Anything that behaves like a bool (*pred)(unsigned int) is acceptable, which is why functors work: they can be called with a single parameter and return a bool.

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

As others have pointed out, the type of checker::isEven is bool (checker::*pred)(unsigned int) which doesn't behave like the original function pointer, because it needs an instance of checker to be called on.

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

A pointer to a member function can be conceptually considered as a regular function pointer that takes an additional argument, the this pointer (e.g. bool (*pred)(checker*, unsigned int)). You can actually generate a wrapper that can be called that way using std::mem_fn(&checker::isEven) (also from <functional>). That still doesn't help you, because now you have a function object that must be called with two parameters rather than only one, which std::find_if still doesn't like.

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

Using std::bind treats the pointer to a member function as if it was a function taking the this pointer as its first argument. The arguments passed to std::bind specify that the first argument should always be &c, and the second argument should bind to the first argument of the newly returned function object. This function object is a wrapper that can be called with one argument, and can therefore be used with std::find_if.

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

Although the return type of std::bind is unspecified, you can convert it to a std::function<bool(unsigned int)> (in this particular case) if you need to refer to the bound function object explicitly rather than passing it straight to another function like I did in my example.

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

#3


4  

checker::isEven is not a function; it is a member function. And you cannot call a non-static member function without a reference to a checker object. So you can't just use a member function in any old place that you could pass a function pointer. Member pointers have special syntax that requires more than just () to call.

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

That's why functors use operator(); this makes the object callable without having to use a member function pointer.

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

#4


1  

The example given says you should use the call operator (operator()) whereas in your example you've called your function isEven. Try re-writing it as:

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

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

#5


1  

I prefer functors (function objects) because make your program more readable and, more importantly, expressing the intent clearly.

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

This is my favorite example:

这是我最喜欢的例子:

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<>());

Note: In recent years we've got a lambda expression support.

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

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

#1


4  

I guess it's because the type of c.isEven() is,

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

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

which may not be expected by find_if(). std::find_if should be expecting either a function pointer (bool (*)(unsigned int)) or a function object.

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

Edit: Another constraint: A non-static member function pointer must be called by the class object. In your case, even if you succeed to pass the member function then still find_if() will not have any information about any checker object; so it doesn't make sense to have find_if() overloaded for accepting a member function pointer argument.

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

Note: In general c.isEven is not the right way to pass member function pointer; it should be passed as, &checker::isEven.

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

#2


10  

A pointer to a member function requires an instance to be called on, and you are passing only the member function pointer to std::find_if (actually your syntax is incorrect, so it doesn't work at all; the correct syntax is std::find_if(itBegin, itEnd, &checker::isEven) which then still doesn't work for the reasons I gave).

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

The find_if function expects to be able to call the function using a single parameter (the object to test), but it actually needs two to call a member function: the instance this pointer and the object to compare.

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

Overloading operator() allows you to pass both the instance and the function object at the same time, because they're now the same thing. With a member function pointer you must pass two pieces of information to a function that expects only one.

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

There is a way to do this using std::bind (which requires the <functional> header):

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

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

If your compiler doesn't support std::bind, you can also use boost::bind for this. Though there's no real advantage to doing this over just overloading operator().

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


To elaborate a bit more, std::find_if expects a function pointer matching the signature bool (*pred)(unsigned int) or something that behaves that way. It doesn't actually need to be a function pointer, because the type of the predicate is bound by the template. Anything that behaves like a bool (*pred)(unsigned int) is acceptable, which is why functors work: they can be called with a single parameter and return a bool.

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

As others have pointed out, the type of checker::isEven is bool (checker::*pred)(unsigned int) which doesn't behave like the original function pointer, because it needs an instance of checker to be called on.

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

A pointer to a member function can be conceptually considered as a regular function pointer that takes an additional argument, the this pointer (e.g. bool (*pred)(checker*, unsigned int)). You can actually generate a wrapper that can be called that way using std::mem_fn(&checker::isEven) (also from <functional>). That still doesn't help you, because now you have a function object that must be called with two parameters rather than only one, which std::find_if still doesn't like.

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

Using std::bind treats the pointer to a member function as if it was a function taking the this pointer as its first argument. The arguments passed to std::bind specify that the first argument should always be &c, and the second argument should bind to the first argument of the newly returned function object. This function object is a wrapper that can be called with one argument, and can therefore be used with std::find_if.

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

Although the return type of std::bind is unspecified, you can convert it to a std::function<bool(unsigned int)> (in this particular case) if you need to refer to the bound function object explicitly rather than passing it straight to another function like I did in my example.

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

#3


4  

checker::isEven is not a function; it is a member function. And you cannot call a non-static member function without a reference to a checker object. So you can't just use a member function in any old place that you could pass a function pointer. Member pointers have special syntax that requires more than just () to call.

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

That's why functors use operator(); this makes the object callable without having to use a member function pointer.

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

#4


1  

The example given says you should use the call operator (operator()) whereas in your example you've called your function isEven. Try re-writing it as:

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

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

#5


1  

I prefer functors (function objects) because make your program more readable and, more importantly, expressing the intent clearly.

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

This is my favorite example:

这是我最喜欢的例子:

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<>());

Note: In recent years we've got a lambda expression support.

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

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