这两个高阶函数定义之间是否有任何差异?

fle*_*lee 45 c c++

主要的4个陈述之间是否有任何差异?我觉得只有apply2(&func)才有意义.但是,所有4都返回相同的值.

int func(void) 
{
    return 1;
}

int apply1( int f1(void) )
{
    return f1();
}

int apply2( int (*f1) (void) ) 
{
    return f1();
}

int main() 
{
    apply1(func); 
    apply1(&func);
    apply2(func);
    apply2(&func);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

zne*_*eak 70

首先,函数指针很难.认为你可以将函数作为参数传递给另一个函数需要一些类似于理解递归的思维弯曲.一开始你不会得到它,但突然之间,就像你的大脑中开启的理解闸门一样,你开悟了.

但是,您仍然必须知道在C和C++中将函数作为参数传递的规则.在这些语言中,功能不是一等公民,所以对你可以用它们做什么有很多限制.

句法

函数指针语法有点难看.基本的解剖学是[return type] (*[name])([argument list]).周围的括号*name是消除函数指针和返回指针的函数之间消除歧义的必要条件:

// not function pointers: * not grouped to function name
int x(); // function that returns an int
int* x(); // function that returns an int*
int *x(); // also a function that returns an int*, spaces don't matter

// function pointers: * grouped to function name
int (*x)(); // pointer to a function that returns an int
int* (*x)(); // pointer to a function that returns an int*
Run Code Online (Sandbox Code Playgroud)

衰变

在作为参数传递方面,函数的行为与数组大致相同.传递后,它们会变为指针.相比:

void Foo(int bar[4]); // equivalent to: void Foo(int* bar)
void Bar(int baz()); // equivalent to: void Bar(int (*baz)())
Run Code Online (Sandbox Code Playgroud)

这只是因为函数和数组是不可赋值和不可复制的:

int foo[4];
int bar[4] = foo; // invalid

int foo();
int bar() = foo; // invalid
Run Code Online (Sandbox Code Playgroud)

因此,将它们作为函数参数传递的唯一方法是传递它们的地址而不是复制它们.(这对于数组来说是有争议的,但这就是它的工作原理.)这些"值"在作为参数传递时转换为指针的事实被称为"衰变".

这两个原型是兼容的(也就是说,它们指的是相同的功能,而不是不同的重载),因此,两者之间没有区别:

int foo(void bar());
int foo(void (*bar)());
Run Code Online (Sandbox Code Playgroud)

除了视觉效果之外,这两个声明之间绝对没有区别.由于衰减,两个函数都接受函数指针,无论它是否看起来如此.虽然,由于衰变通常被认为是令人讨厌和令人困惑的事情,大多数开发人员更愿意明确地要求一个函数指针(许多开发人员甚至不知道函数类型会衰减).

隐含转换

现在,关于将函数作为参数传递.这个只是衰变的结果:函数必须可以隐式转换为它们的函数指针类型.这意味着您可以传递一个需要函数指针的函数,编译器将为您获取其地址.为此,这些再一次是相同的:

int foo();
int (*bar)() = foo; // the compiler implicitly assigns the address of foo to bar
int (*baz)() = &foo; // you explicitly assign the address of foo to baz
Run Code Online (Sandbox Code Playgroud)

结合这两个解释,你会发现你的四个函数调用都是一样的.apply1并且apply2都接受相同类型的参数(int (*)(void)),即使它不明显apply1; 当你用func而不是调用函数时&func,编译器隐式地为你获取地址并使它等效于&func.


以下内容超出了问题的范围,但它详细阐述了前一部分,我认为它有点整洁.

功能参考[仅限C++]

这是一个鲜为人知的事实,但也可以传递对数组和函数的引用:在这种情况下,不会发生衰减.像这样:

void Foo(int (&bar)[4]); // NOT equivalent to void Foo(int* bar)
void Bar(int (&baz)()); // NOT equivalent to void Bar(int (*baz)())
Run Code Online (Sandbox Code Playgroud)

在这种情况下,不允许使用address-of运算符,因为指针类型和引用类型之间没有隐式转换.击败腐烂通常被视为一件好事,因为腐烂往往令人困惑.

int baz();
Bar(baz); // valid
Bar(&baz); // INVALID
Run Code Online (Sandbox Code Playgroud)

函数引用遵循与普通引用相同的规则:它们只能在定义时分配,不能为空.

类型定义

你可以使用的函数指针不那么难看typedef.

typedef int (*X)();
X func; // func is a pointer to a function that returns an int
Run Code Online (Sandbox Code Playgroud)

如果你拿出这个(*)部分,事情会变得更有趣:

typedef int X();
X* func; // func is a function pointer
X& func; // func is a function reference [C++ only]
X func; // func is a function declaration (!!)
Run Code Online (Sandbox Code Playgroud)

在后一种情况下,X func;相当于一个声明说int func();.不要在家里这样做,除非你想把每个人都搞糊涂了.

decltype 有所作为[仅限C++]

函数和函数指针之间的另一个有趣的区别在于使用decltype.decltype"返回"表达的类型.对于这种结构,有之间的差异function&function:

int bar();
decltype(bar); // type is int ()
decltype(&bar); // type is int (*)()
Run Code Online (Sandbox Code Playgroud)

如果要将类型作为模板参数传递给,那么这种差异尤其重要std::unique_ptr.

std::unique_ptr<void, decltype(free)> foo; // INVALID
std::unique_ptr<void, decltype(&free)> foo; // valid
Run Code Online (Sandbox Code Playgroud)

第一个是无效的,因为它会尝试创建一个函数作为实例字段unique_ptr.

  • "指向数组的指针"让我感到畏缩.我知道你在说什么,但它很混乱,因为`p`是一个"指向数组的指针",如果它被声明,例如,`int(*p)[4]`.衰减为`int*p`的数组名称变为指向第一个元素的指针,而不是整个数组. (3认同)