45 votes

Existe-il des différences entre ces deux fonction d'ordre supérieur définitions?

Existe-il des différences entre les 4 états principal? Je me sens seule apply2(&func) fait sens. Cependant, tous les 4 retourner la même valeur.

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

70voto

zneak Points 45458

Tout d'abord, les pointeurs de fonction sont durs. Penser que vous pouvez passer une fonction en paramètre à une autre fonction nécessite un certain esprit de flexion similaire à la compréhension de la récursivité. Vous n'aurez pas la au début, mais ensuite tout d'un coup, c'est comme les vannes de la compréhension de l'ouvrir dans votre cerveau et vous êtes éveillé.

Mais alors, vous devez connaître les règles de passage de fonctions en tant que paramètres en C et C++. Dans ces langues, les fonctions ne sont pas des citoyens de première classe, donc il ya beaucoup de restrictions sur ce que vous pouvez faire avec eux.

La syntaxe

Le pointeur de fonction la syntaxe est un peu moche. L'anatomie de base est de [return type] (*[name])([argument list]). Les parenthèses autour de *name sont nécessaires pour lever l'ambiguïté entre un pointeur de fonction et une fonction retournant un pointeur:

// 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*

La carie

En termes de passage de paramètres, des fonctions se comportent sur le même sous forme de tableaux. Lorsqu'ils sont passés, ils changent en un pointeur. Comparer:

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

C'est tout simplement parce que les fonctions et les tableaux sont non-cessible et non-copiable:

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

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

Par conséquent, la seule façon de les faire passer comme paramètres de la fonction est de transmettre leur adresse au lieu de les copier. (Ce qui est discutable pour les tableaux, mais c'est la façon dont il fonctionne.) Le fait que ces "valeurs" sont transformés en des pointeurs quand passés comme paramètres est appelée "pourriture".

Ces deux prototypes sont compatibles (qui est, ils se réfèrent à la même fonction, ne diffère pas des surcharges), et par conséquent, il n'y a pas de différence entre les deux:

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

Les visuels de côté, il n'y a absolument aucune différence entre ces deux déclarations. Les deux fonctions accepter une fonction de pointeur, il semble comme il ou non, en raison de la désintégration. Cependant, depuis la carie est souvent considéré comme un méchant et qui prête à confusion, la plupart des développeurs préfèrent demander explicitement un pointeur de fonction (et beaucoup de développeurs ne sait même pas de fonction des types de désintégration).

Les Conversions Implicites

Maintenant, sur le passage de fonctions en tant que paramètres. Il s'agit simplement d'une conséquence de la décroissance: les fonctions doivent être implicitement converti à leur fonction de type pointeur. Cela signifie que vous pouvez passer une fonction où un pointeur de fonction qui est attendu, et le compilateur va obtenir son adresse pour vous. À cet effet, elles sont, une fois encore, du même:

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

Combiner ces deux explications, et vous vous rendrez compte que vos quatre appels de fonction sont tous les mêmes. apply1 et apply2 acceptent tous deux le même type de paramètre (int (*)(void)), même si ce n'est pas évident pour apply1; et lorsque vous appelez les fonctions func au lieu de &func, le compilateur se fait implicitement l'adresse pour vous et le rend équivalent à &func.


Ce qui suit est hors de la portée de la question, mais il donne des précisions sur la partie précédente, et je pense que c'est chouette.

Les Références de fonction [C++]

Il est un fait peu connu, mais il est aussi possible de passer des références à des tableaux et des fonctions: dans ce cas, pas de chute se produit. Comme ceci:

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

Dans ce scénario, vous n'êtes pas autorisé à utiliser l'adresse de l'opérateur, car il n'y a pas de conversion implicite entre les types pointeur et les types référence. Vaincre la carie est généralement considéré comme une bonne chose, que la pourriture est souvent source de confusion.

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

Les références de fonction obéir aux mêmes règles que la normale références: ils ne peuvent être attribués qu'à la définition du temps, et ne peut pas être null.

Typedefs

Vous pouvez faire des pointeurs de fonction moins moche à l'aide de typedef.

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

Les choses sont encore plus intéressantes si vous sortez de la (*) partie:

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

Dans ce dernier cas, X func; est équivalent à une déclaration disant int func();. Ne faites pas cela à la maison, sauf si vous voulez confondre l'enfer hors de tout le monde.

decltype fait une différence [C++]

Une autre différence intéressante entre les fonctions et les pointeurs de fonction se pose avec l'utilisation de l' decltype. decltype "retourne" le type d'une expression. Pour cette construction, il y est une différence entre function et &function:

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

Cette différence est particulièrement important si vous voulez passer le type d'un paramètre du modèle, disons, d' std::unique_ptr.

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

La première n'est pas valide car elle aurait pour but de créer une fonction comme une instance domaine de l' unique_ptr.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X