C++中的函数指针是函数对象吗?

11

C++标准将函数对象定义为:

 

函数对象类型是可以作为后缀表达式出现在函数调用中的对象类型。(链接

一开始我认为函数对象就是函数符,但后来我意识到对于一个类型为P(而不是函数,而是函数指针)的函数指针ptrstd::is_object_v<P>true并且可以使用ptr(Args...)语法进行调用。

我正确地理解了该标准将函数指针视为函数对象吗?如果不是,哪部分定义未被函数指针满足?

3个回答

17

是的,它们是。在C++标准中,“对象”一词并不意味着面向对象编程中的“对象”。一个int就是一个对象。


7
C++标准中没有使用术语“函数对象”(functor)(因此也没有给出该术语的定义)。大多数人会将“函数对象”定义为包括所有不是函数指针的函数对象。 - Ben Voigt
@Ben Voigt 这有点误导人,因为术语的原始含义是将一组映射到另一组,例如在函数式编程中作为纯函数实现。 在我看来,“函子”这个术语必须消失,我们应该使用“可调用对象”这个术语。 - Swift - Friday Pie
@Swift-FridayPie 虽然我同意应该避免使用“函子”这个术语,但不幸的是,“可调用对象”也包括函数指针。 - Yongwei Wu

2
函数指针就像它听起来的那样:指向函数的指针。本身是一个包含指针对象的存储器,返回函数类型的可调用对象。
如果您花时间阅读标准的前几章,您将了解任何变量声明都声明了包含对象的某种类型的存储器。这些可以是原始类型或类的对象。基本上,C++中可以存储的任何东西都是对象。
通过声明函数指针,您创建了可以存储该函数地址的存储器,并且可以使用operator()。
通过lambda表达式可以创建另一种类型的可调用闭包。它们不是函数对象,每个表达式都创建一个唯一的可调用对象,但无捕获的lambda可以用作一个,例如将其分配给函数指针,例如:
double (*square)(double) = [](double a)->double { return a*a; };
之后,您可以使用类似于square(3.6)的表达式来调用它。
对于函数和lambda调用运算符operator()由语言提供,通过为类定义operator(),您创建了人们通常称之为“函数对象”的内容,这是一个错误的说法,因为数学或Haskell等语言中的实际函数器不存储状态。编译器创建的lambda表达式的结果是一个“函数对象”,它存储捕获对象的状态。
将这些对象命名为可调用可能有点误导,因为作为C++中的一个概念,可调用对象是可以与INVOKE操作一起使用的任何对象,其中甚至包括数据成员的指针,即使没有函数调用发生。
如果我们可以使用所述对象进行函数调用,则只剩下一种选择,那就是它是函数对象。它可以是函数、lambda表达式、函数对象、函数指针、具有指定类实例的成员函数指针(obj.*memberptr或objptr->*memberptr-成员函数的调用非常特殊)-它们都是函数对象。

一个函数指针是指向函数的指针,而不是指向函数对象的指针。指向函数对象的指针是对象指针。函数不是一个对象。声明函数并不描述一个函数对象。最后,您不能使用operator()来调用成员函数指针,因此它也不是一个函数对象。 - Ben Voigt
@BenVoigt 我承认我错了。应该不是“opan erator”,而是“expression”... 在后一种情况下,要使用的表达式也很奇怪。 - Swift - Friday Pie

0
函数指针是函数对象,但它们也有一个有趣的怪癖,来自C时代。
#include <iostream>

int foo(int a)
{
    std::cout << "Hello, " << a << " stars\n";
    return a;
}
    
int (*pr) (int) = foo;

int main()
{
    pr(0);
    (*pr)(1);
    (**pr)(2);
    (***pr)(3);
    return 0;
}

后缀表达式应具有函数类型或函数指针类型,并且在需要进行此类转换时,函数可在语境上转换为函数指针,因此重复解引用运算符会将类型“来回”反弹,使其成为函数指针。无捕获的 Lambda 也可以。
// also can write `auto pr2 = ...`
int (*pr2) (int) = [](int a)->int { 
         std::cout << "Hello, lambda " << a <<  " stars\n"; return a; 
};

// in main()
(**pr2)(2);

如果pr是一个泛函,那么这段代码自然是不合法的。

int b = 2;
auto pr3 = [=]()->int { 
   std::cout << "Hello, general lambda " << b << std::endl; 
   return b; 
};

pr3();
//(*pr3)();  Ill-formed: not a pointer!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接