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

发布于 2024-11-26 20:31:35 字数 761 浏览 1 评论 0原文

我正在尝试编写与 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

流星番茄 2024-12-03 20:31:35

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

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

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

有一种方法可以使用 std::bind (需要 标头)来执行此操作:

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)(同样来自 )生成一个可以以这种方式调用的包装器。这仍然对您没有帮助,因为现在您有一个必须使用两个参数而不是只有一个参数来调用的函数对象,而 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 is std::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 instance this 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):

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


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.

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.

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.

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.

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.

狼性发作 2024-12-03 20:31:35

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

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

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,

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.

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.

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

时光瘦了 2024-12-03 20:31:35

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

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

£烟消云散 2024-12-03 20:31:35

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

这是我最喜欢的例子:

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

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.

// Same example with lambda expression
double result = accumulate(cbegin(nums), cend(nums), 1.1,
                            [](double x, double y) { return x * y; });
梦醒灬来后我 2024-12-03 20:31:35

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

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

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:

class checker { 
    public: 
       bool operator()(unsigned int i) 
       { return (i%2 == 0); }
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文