在 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);
我对第二种类型有更多的用途,因为我通常想创建一个谓词对象中包含一些成员并在算法中使用它们。当我在检查器中添加相同的 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. 给出错误吗?另外,如果有任何关于谓词和迭代器基础知识的指示,我将不胜感激。
I'm trying to write predicate function for use with STL algorithms. I see that they are two ways to define a predicate:
(1) Use a simple function as below:
bool isEven(unsigned int i) { return (i % 2 == 0); }
std::find_if(itBegin, itEnd, isEven);
(2) Use the operator() function as below:
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:
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
指向成员函数的指针需要调用一个实例,并且您仅将成员函数指针传递给
std::find_if
(实际上您的语法不正确,因此它根本不起作用;正确的语法是std::find_if(itBegin, itEnd, &checker::isEven)
由于我给出的原因,它仍然不起作用)。find_if
函数期望能够使用单个参数(要测试的对象)来调用该函数,但实际上它需要两个参数来调用成员函数:实例this
指针和要比较的对象。重载
operator()
允许您同时传递实例和函数对象,因为它们现在是同一件事。使用成员函数指针,您必须将两条信息传递给只需要一条信息的函数。有一种方法可以使用
std::bind
(需要
标头)来执行此操作:如果您的编译器不支持
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)
(同样来自
)生成一个可以以这种方式调用的包装器。这仍然对您没有帮助,因为现在您有一个必须使用两个参数而不是只有一个参数来调用的函数对象,而std::find_if
仍然不喜欢这一点。使用 std::bind 会将指向成员函数的指针视为将 this 指针作为其第一个参数的函数。传递给
std::bind
的参数指定第一个参数应始终为&c
,第二个参数应绑定到新返回的函数对象的第一个参数。该函数对象是一个包装器,可以使用一个参数调用,因此可以与 std::find_if 一起使用。尽管
std::bind
的返回类型未指定,但您可以将其转换为std::function
(在本例中)如果您需要显式引用绑定函数对象,而不是像我在示例中那样将其直接传递给另一个函数。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 isstd::find_if(itBegin, itEnd, &checker::isEven)
which then still doesn't work for the reasons I gave).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 instancethis
pointer and the object to compare.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.There is a way to do this using
std::bind
(which requires the<functional>
header):If your compiler doesn't support
std::bind
, you can also useboost::bind
for this. Though there's no real advantage to doing this over just overloadingoperator()
.To elaborate a bit more,
std::find_if
expects a function pointer matching the signaturebool (*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 abool (*pred)(unsigned int)
is acceptable, which is why functors work: they can be called with a single parameter and return abool
.As others have pointed out, the type of
checker::isEven
isbool (checker::*pred)(unsigned int)
which doesn't behave like the original function pointer, because it needs an instance ofchecker
to be called on.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 usingstd::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, whichstd::find_if
still doesn't like.Using
std::bind
treats the pointer to a member function as if it was a function taking thethis
pointer as its first argument. The arguments passed tostd::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 withstd::find_if
.Although the return type of
std::bind
is unspecified, you can convert it to astd::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.我猜这是因为
c.isEven()
的类型是find_if()
可能不期望的。std::find_if
应该是函数指针 (bool (*)(unsigned int)
) 或函数对象。编辑:另一个约束:非
静态
成员函数指针必须由类
对象调用。在您的情况下,即使您成功传递了成员函数,find_if()
仍然不会获得有关任何checker
对象的任何信息;因此,重载find_if()
来接受成员函数指针参数是没有意义的。注意:一般来说,
c.isEven
不是传递成员函数指针的正确方法;它应该作为&checker::isEven
传递。I guess it's because the type of
c.isEven()
is,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.Edit: Another constraint: A non-
static
member function pointer must be called by theclass
object. In your case, even if you succeed to pass the member function then stillfind_if()
will not have any information about anychecker
object; so it doesn't make sense to havefind_if()
overloaded for accepting a member function pointer argument.Note: In general
c.isEven
is not the right way to pass member function pointer; it should be passed as,&checker::isEven
.checker::isEven
不是一个函数;它是一个成员函数。如果没有对checker
对象的引用,则无法调用非静态成员函数。因此,您不能在任何可以传递函数指针的旧位置使用成员函数。成员指针具有特殊的语法,需要的不仅仅是()
来调用。这就是函子使用
operator()
的原因;这使得对象可以调用而无需使用成员函数指针。checker::isEven
is not a function; it is a member function. And you cannot call a non-static member function without a reference to achecker
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.That's why functors use
operator()
; this makes the object callable without having to use a member function pointer.我更喜欢 函子(函数对象),因为使您的程序更具可读性更重要的是,清楚地表达意图。
这是我最喜欢的例子:
注意:近年来,我们有了 lambda 表达式 支持。
I prefer functors (function objects) because make your program more readable and, more importantly, expressing the intent clearly.
This is my favorite example:
Note: In recent years we've got a lambda expression support.
给出的示例表明您应该使用调用运算符 (
operator()
),而在您的示例中您已调用函数isEven
。尝试将其重写为:The example given says you should use the call operator (
operator()
) whereas in your example you've called your functionisEven
. Try re-writing it as: