Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.
surcharge_fonctions [2016/08/23 01:12] gbdivers |
surcharge_fonctions [2016/08/30 12:59] (Version actuelle) gbdivers |
||
---|---|---|---|
Ligne 2: | Ligne 2: | ||
^ [[references|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[fonctions_generiques|Chapitre suivant]] ^ | ^ [[references|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[fonctions_generiques|Chapitre suivant]] ^ | ||
- | ====== [Aller plus loin] La surcharge de fonctions et résolution des noms ====== | + | ====== La surcharge de fonctions et résolution des noms ====== |
===== Plusieurs fonctions avec le même nom ===== | ===== Plusieurs fonctions avec le même nom ===== | ||
Ligne 105: | Ligne 105: | ||
L'étude des opérateurs et leur surcharge est suffisamment important pour être détaillé dans un chapitre dédié, dans la partie sur la programmation orientée objet. | L'étude des opérateurs et leur surcharge est suffisamment important pour être détaillé dans un chapitre dédié, dans la partie sur la programmation orientée objet. | ||
+ | |||
+ | |||
+ | ==== Cas particulier des references ==== | ||
+ | |||
+ | Vous avez vu dans le chapitre précédent qu'il existe plusieurs types de passage de valeurs dans une fonction : par valeur ou par références (constante ou non, //lvalue// ou //rvalue//). | ||
+ | |||
+ | Le point important a retenir est quelle type de paramètre accepte quel type d'argument. Cela était résumé dans le tableau suivant : | ||
+ | |||
+ | ^ Passage ^ lvalue ^ rvalue ^ | ||
+ | | Par valeur | oui | oui | | ||
+ | | Référence constante | oui | oui | | ||
+ | | Référence | oui | **non** | | ||
+ | | Rvalue-reference | **non** | oui | | ||
+ | |||
+ | Il est possible de surcharger des fonctions pour écrire du code spécifique, selon si la fonction est appellee avec une //lvalue// (une variable) ou une //rvalue// (un temporaire). | ||
+ | |||
+ | Mais pour éviter les ambiguïtés lors de l'appel de fonction, il ne faut pas écrire deux fonctions qui acceptent le même type de valeurs. Par exemple, si vous écrivez : | ||
+ | |||
+ | <code cpp> | ||
+ | void f(int i); // passage par valeur | ||
+ | void f(int && i); // passage par rvalue-reference | ||
+ | </code> | ||
+ | |||
+ | Si vous appelez cette fonction avec une //lvalue//, il n'y aura pas d'ambiguïté (seul le passage par valeur sera valide). Si vous appelez avec une //rvalue//, il y a ambiguïté (les deux versions de la fonction acceptent les //rvalues//). | ||
+ | |||
+ | Il y a donc que trois approches possibles : | ||
+ | |||
+ | 1. Si vous ne voulez pas écrire de code spécifique //lvalue// vs //rvalue// et que la copie n'est pas un problème (par exemple pour les types fondamentaux comme ''int'', ''double'', etc.), alors vous utilisez le passage par valeur. | ||
+ | |||
+ | <code cpp> | ||
+ | #include <iostream> | ||
+ | |||
+ | void f(int i) { | ||
+ | std::cout << "f(int i): " << i << std::endl; | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | int i { 123 }; | ||
+ | f(i); | ||
+ | f(456); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | affiche : | ||
+ | |||
+ | <code> | ||
+ | f(int i): 123 | ||
+ | f(int i): 456 | ||
+ | </code> | ||
+ | |||
+ | 2. Si vous ne voulez pas écrire de code spécifique //lvalue// vs //rvalue// et que la copie est un problème (par exemple pour les classes comme ''std::string'', ''std::vector'', etc.), alors vous utilisez le passage par référence constante. | ||
+ | |||
+ | <code cpp> | ||
+ | #include <iostream> | ||
+ | #include <string> | ||
+ | |||
+ | void f(std::string const& s) { | ||
+ | std::cout << "f(std::string const& s): " << s << std::endl; | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | std::string s { "hello" }; | ||
+ | f(s); | ||
+ | f("world"); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | affiche : | ||
+ | |||
+ | <code> | ||
+ | f(std::string const& s): hello | ||
+ | f(std::string const& s): world | ||
+ | </code> | ||
+ | |||
+ | 3. Si vous voulez écrire de code spécifique //lvalue// vs //rvalue//, alors vous utilisez le passage par référence non constante (c'est a dire que vous écrivez deux fonctions surchargées, qui acceptent une //lvalue-reference// et une //rvalue-reference//). | ||
+ | |||
+ | <code cpp> | ||
+ | #include <iostream> | ||
+ | |||
+ | void f(int & i) { | ||
+ | std::cout << "f(int & i): " << i << std::endl; | ||
+ | } | ||
+ | |||
+ | void f(int && i) { | ||
+ | std::cout << "f(int && i): " << i << std::endl; | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | int i { 123 }; | ||
+ | f(i); | ||
+ | f(456); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | affiche : | ||
+ | |||
+ | <code> | ||
+ | f(int & i): 123 | ||
+ | f(int && i): 456 | ||
+ | </code> | ||
+ | |||
+ | Ce type de surcharge de fonction sera particulièrement intéressant lorsque vous concevrez vos propres classes, puisque cela permet d'optimiser la gestion des données internes, selon le type de valeurs utilisées. C'est ce qui est fait dans la bibliothèque standard. Par exemple, pour ''std::vector'', vous pouvez voir dans la documentation ([[http://en.cppreference.com/w/cpp/container/vector/vector|std::vector::vector]]) : | ||
+ | |||
+ | <code> | ||
+ | vector( const vector& other ); (5) | ||
+ | vector( vector&& other ) (6) | ||
+ | </code> | ||