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

时间:2013-09-05 00:50:07

标签: 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;
}

1 个答案:

答案 0 :(得分: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*

衰减

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

void Foo(int bar[4]); // equivalent to: void Foo(int* bar)
void Bar(int baz()); // equivalent to: void Bar(int (*baz)())

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

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

int foo();
int bar() = foo; // invalid

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

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

int foo(void bar());
int foo(void (*bar)());

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

隐含转化

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

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

结合这两个解释,你会发现你的四个函数调用都是一样的。 apply1apply2都接受相同类型的参数(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)())

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

int baz();
Bar(baz); // valid
Bar(&baz); // INVALID

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

类型定义

使用typedef可以使函数指针不那么难看。

typedef int (*X)();
X func; // func is a pointer to a function that returns an int

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

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

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

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

函数和函数指针之间的另一个有趣的区别在于使用decltypedecltype“返回”表达的类型。对于此构造, function&function之间的差异:

int bar();
decltype(bar); // type is int ()
decltype(&bar); // type is int (*)()

如果您想将类型作为模板参数传递给std::unique_ptr,这种差异尤为重要。

std::unique_ptr<void, decltype(free)> foo; // INVALID
std::unique_ptr<void, decltype(&free)> foo; // valid

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