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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6854039/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 20:46:34  来源:igfitidea点击:

Correct Way to Define a Predicate Function in C++

c++stlpredicate

提问by cppcoder

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:

我对第二种类型有更多的用处,因为我通常想创建一个包含一些成员的谓词对象,并在算法中使用它们。当我在检查器中添加相同的 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. 给出错误吗?另外,如果您有任何有关谓词和迭代器基础知识的指针,我将不胜感激。

采纳答案by iammilind

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_ifshould 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-staticmember function pointer must be called by the classobject. In your case, even if you succeed to pass the member function then still find_if()will not have any information about any checkerobject; so it doesn't make sense to have find_if()overloaded for accepting a member function pointer argument.

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

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

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

回答by Sven

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)which then 仍然不根据我给出的原因工作)。

The find_iffunction 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 thispointer 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(需要<functional>标题)来做到这一点:

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::bindfor 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_ifexpects 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)或具有这种行为的东西。它实际上并不需要是一个函数指针,因为谓词的类型是由模板绑定的。任何行为类似于 a 的东西bool (*pred)(unsigned int)都是可以接受的,这就是函子工作的原因:它们可以用单个参数调用并返回 a bool

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

正如其他人指出的那样,checker::isEvenis的类型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 thispointer (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_ifstill doesn't like.

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

Using std::bindtreats the pointer to a member function as if it was a function taking the thispointer as its first argument. The arguments passed to std::bindspecify 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.

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

Although the return type of std::bindis 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<bool(unsigned int)>如果您需要显式引用绑定函数对象而不是像我在示例中那样直接将其传递给另一个函数,则可以将其转换为 a (在这种特殊情况下)。

回答by Nicol Bolas

checker::isEvenis not a function; it is a member function. And you cannot call a non-static member function without a reference to a checkerobject. 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对象引用的情况下调用非静态成员函数。所以你不能只在任何可以传递函数指针的旧地方使用成员函数。成员指针具有特殊的语法,需要的不仅仅是()调用。

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

这就是为什么函子使用operator(); 这使得对象无需使用成员函数指针即可调用。

回答by Marko Tunjic

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 expressionsupport.

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

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

回答by Hugh

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()) 而在你的例子中你已经调用了你的 function isEven。尝试将其重写为:

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