【问题标题】:Are function pointers function objects in C++?函数指针是 C++ 中的函数对象吗?
【发布时间】:2018-03-27 01:53:41
【问题描述】:

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

函数对象类型是一种对象类型,它可以是 函数调用中的后缀表达式。 (link)

首先我认为函数对象是函子,但后来我意识到对于 P 类型的函数指针 ptr(不是函数,而是函数指针),std::is_object_v<P>true 并且可以使用ptr(Args...) 语法调用。

函数指针被标准视为函数对象是对的吗?如果它们不是函数指针不满足定义的哪一部分?

【问题讨论】:

    标签: c++ function c++11 typetraits function-object


    【解决方案1】:

    是的,他们是。 C++ 标准中的“对象”一词并不意味着 OOP 意义上的“对象”。 int 是一个对象。

    【讨论】:

    • @Rodrigo:C++ 标准没有使用术语“函子”(因此没有给出定义)。大多数人会以包含所有非函数指针的函数对象的方式定义函子
    • @Ben Voigt 这有点误导,因为术语的原始含义假设从一组映射到另一组,例如在函数式编程中作为纯函数实现。恕我直言,术语“函子”必须死,我们应该使用术语“可调用对象”
    • @Swift-FridayPie 虽然我同意应该避免使用 functor 这个术语,但不幸的是,“可调用对象”也包括函数指针。
    【解决方案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.*memberptrobjptr->*memberptr——成员函数的调用很特殊)——它们都是函数对象。

    【讨论】:

    • 函数指针是指向函数的指针,而不是指向函数对象的指针。指向函数对象的指针是对象指针。函数不是对象。声明一个函数并不描述一个函数对象。最后,不能将operator() 与成员函数指针一起使用,因此它不是函数对象。
    • @BenVoigt 我的立场是正确的。而且它不应该是运算符,而是表达式......在后一种情况下,要使用的表达式也很奇怪
    【解决方案3】:

    函数指针是函数对象,但它们也有一个来自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!
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-13
      相关资源
      最近更新 更多