Le type de retour fait-il partie de la signature de la fonction?

En C ++, le type de retour est-il considéré comme faisant partie de la signature de la fonction? et aucune surcharge n’est autorisée avec le type de retour modifié.

Les fonctions normales n’incluent pas le type de retour dans leur signature.

( note : j’ai réécrit cette réponse, et les commentaires ci-dessous ne s’appliquent pas à cette révision – voir l’historique des modifications pour plus de détails).

introduction

Cependant, la question des fonctions et des déclarations de fonction dans la norme est compliquée. Il y a deux couches à considérer:

  • Déclarations
  • Entités

La déclaration de fonction peut déclarer une entité de fonction ou une entité de modèle. Si une entité de fonction est déclarée, vous devez soit faire une spécialisation explicite d’un modèle de fonction (avec tous les arguments spécifiés), soit une déclaration d’une fonction ordinaire. Si une entité modèle est déclarée, vous déclarez alors un modèle de fonction primaire ou une spécialisation explicite dans laquelle certains arguments ne sont pas spécifiés. (Ceci est très similaire à la relation de “déclaration d’object” et d’objects ou de références: le premier peut déclarer soit un object, soit une référence. Par conséquent, une déclaration d’object ne peut pas nécessairement déclarer un object!).

La norme définit la signature d’une fonction pour inclure les éléments suivants en 1.3.10 :

Les types de ses parameters et, si la fonction est un membre de classe, les qualificateurs cv (le cas échéant) de la fonction elle-même et de la classe dans laquelle la fonction membre est déclarée. La signature d’une spécialisation de modèle de fonction inclut les types de ses arguments de modèle. (14.5.5.1)

Il manque le type de retour dans cette définition, qui fait partie de la signature d’une spécialisation de modèle de fonction (c’est-à-dire une déclaration de fonction qui déclare une fonction qui est une spécialisation d’un modèle), comme indiqué par 14.5.5.1 0x documents de travail corrigés déjà pour mentionner le type de retour dans 1.3.10 aussi):

La signature d’une spécialisation de modèle de fonction consiste en la signature du modèle de fonction et des arguments réels du modèle (spécifiés ou déduits explicitement).

La signature d’un modèle de fonction comprend sa signature de fonction, son type de retour et sa liste de parameters de modèle.

Alors, que contient exactement une signature?

Ainsi, lorsque nous interrogeons sur la signature d’une fonction , nous devons donner deux réponses:

  • Pour les fonctions qui sont des spécialisations de modèles de fonction, la signature inclut le type de retour.
  • Pour les fonctions qui ne sont pas des spécialisations, le type de retour ne fait pas partie de la signature.

Notez, cependant, que le type de retour, dans tous les cas, est une partie importante du type d’une fonction. Autrement dit, ce qui suit n’est pas valide:

 void f(); int (*pf)() = &f; // different types! 

Quand une surcharge est-elle invalide si seul le type de retour est différent?

Les principaux compilateurs rejettent actuellement le code suivant:

 int f(); double f(); // invalid 

Mais acceptez le code suivant:

 template int f(); template double f(); // invalid? 

Cependant, la norme interdit une déclaration de fonction qui ne diffère que par le type de retour (lors de la définition du moment où une surcharge est valide et non). Il ne définit pas précisément ce que signifie “diffère uniquement par type de retour”.


Références de paragraphe standard:

  • Quand une déclaration de fonction peut-elle être surchargée: 13.1
  • Qu’est-ce qu’une déclaration de fonction: 7/2 et 7/5
  • Quelle est la signature d’un modèle de fonction / spécialisation: 14.5.5.1

Pour référence, voici ce que le plus récent projet C ++ 0x n3000 dit à propos de “signature” dans la version 1.3.11 , qui est beaucoup plus complet dans sa couverture des différents types d’entités:

le nom et la liste de types de parameters (8.3.5) d’une fonction, ainsi que la classe ou l’espace de noms dont elle est membre. Si un modèle de fonction ou de fonction est un membre de classe, sa signature inclut en plus les qualifiants cv (le cas échéant) et le qualificatif de référence (le cas échéant) sur la fonction ou le modèle de fonction lui-même. La signature d’un modèle de fonction inclut en outre son type de retour et sa liste de parameters de modèle. La signature d’une spécialisation de modèle de fonction inclut la signature du modèle dont il s’agit d’une spécialisation et ses arguments de modèle (qu’ils soient explicitement spécifiés ou déduits). [Remarque: les signatures servent de base à la gestion et à la liaison de noms. – note finale]

Cela dépend si la fonction est un modèle de fonction ou non.

Dans C ++ Templates – les guides complets , Jusuttis fournit une définition différente de celle donnée dans le standard C ++, mais avec des conséquences équivalentes:

Nous définissons la signature d’une fonction comme les informations suivantes:

  1. Le nom non qualifié de la fonction
  2. L’étendue de classe ou d’ espace de noms de ce nom, et si le nom a un lien interne, l’unité de traduction dans laquelle le nom est déclaré
  3. La qualification const , volatile ou const volatile de la fonction
  4. Les types de parameters de fonction
  5. son type de retour, si la fonction est générée à partir d’un modèle de fonction
  6. Les parameters du template et les arguments du template , si la fonction est générée à partir d’un template de fonction

Comme nous l’ avons suggéré, il convient de préciser pourquoi le type de retour fait partie de la signature d’une fonction de modèle.

Les fonctions peuvent coexister dans un programme si elles ont des signatures distinctes.

. Cela dit, si le type de retour est un paramètre de modèle:

 template  T foo(int a) {return T();} 

il est possible d’instancier deux fonctions qui ne diffèrent que par le type de retour:

 foo(0); foo(0); 

Non seulement: comme indiqué à juste titre par litb , il est également possible de surcharger deux fonctions de modèle, qui ne diffèrent que par le type de retour, même si le type de retour n’est pas dépendant. Voici son exemple:

 template int foo(T) {} template bool foo(T) {} // at the instantiation point it is necessary to specify the cast // in order not to face ambiguous overload ((int(*)(char))foo)('a'); 

Ils constituent une partie du type suffisante pour surcharger les fonctions en fonction des types de pointeurs de fonctions qui ne diffèrent que par le type de retour:

 int IntFunc() { return 0; } char CharFunc() { return 0; } void FuncFunc(int(*func)()) { cout << "int\n"; } void FuncFunc(char(*func)()) { cout << "char\n"; } int main() { FuncFunc(&IntFunc); // calls void FuncFunc(int_func func) FuncFunc(&CharFunc); // calls void FuncFunc(char_func func) }