Les paramètres en C++
Tags: cpp, french
Published: 2022-01-25 10:43:19

---
layout: post
title: Les paramètres en C++
description: Comment passer un argument à une fonction, par copie ? Par référence immuable ? On va démystifier tout ça ensemble.
tags: [Cpp, French]
published: true
---
## Mise en contexte
Le C++ est un langage puissant, multi-paradigme avec des fonctionnalités de haut niveau mais permettant aussi de faire du code de bas niveau et de l’encapsuler. Mais cela peut le rendre difficile à appréhender, sans compter des syntaxes qui peuvent paraître plus ou moins barbares aux yeux des non-initiés. Il est temps de corriger ça.

Dans cet article, nous allons parler de comment choisir la meilleure manière de passer des arguments à une fonction, et la meilleure manière de renvoyer la valeur de retour. Cela peut paraitre évident, mais en C++, il y a quelques particularités qui peuvent rendre la chose moins triviale qu’il n’y parait.

Prenons une classe toute simple contenant une __std::string__ et un int, avec des méthodes pour récupérer et modifier ces attributs:
```C++
class an_amazing_class
{
    public:
        int     get_int() const;
        void    set_int(int new_int);

        const std::string&  get_str() const;
        void                set_str(const std::string& str);

    private:
        int        _int;
        std::string _str;
};
```

On peut voir que les prototypes des deux méthodes concernant la std::string sont assez différentes de celles concernant l’__int__. Pour l’entier on utilise le type seul comme type de l’argument et type de retour, mais pour la chaine de caractère on rajoute un _« const »_ devant et un _« & »_ après.

Pourquoi ? Qu’est ce que cela veut dire ? Vous commencez peut-être à vous dire que le C++ est compliqué et que vous feriez mieux d’utiliser un autre langage, mais ne vous inquiétez pas : nous allons éclaircir tout ça dans la suite de l’article.

## La copie
Commençons par le cas qui semble le plus simple : l’entier, on a juste écrit son type sans fioritures autour. Cela veut donc dire que la valeur utilisée en argument/renvoyée comme valeur de retour est une copie. Concrètement cela veut dire que le constructeur par copie est appelé, une nouvelle variable est créée avec la même valeur, pour un cas aussi simple qu’un entier, cela copie donc la valeur de notre entier dans un nouvel entier qui est envoyé/retourné par la suite.

Note : si c’est un type défini par le langage (les nombres, références, pointeurs, tableaux et les énumérations), il n’y a techniquement pas de constructeur par copie mais le comportement est le même. [Source](https://stackoverflow.com/questions/5113365/do-built-in-types-have-default-constructors)

## Les références immutables
Voyons maintenant le _« & »_. Il signifie qu’il s’agit d’une référence, donc au lieu d’avoir une copie de la variable, on a l’adresse de la variable pour pouvoir y accéder, le mot clef _« const »_ signifie qu’on ne peut pas la modifier. Finalement, cela envoie l’adresse de la variable et cette variable ne peut pas être modifiée.

## Comparaison
Pour résumer, la copie crée une nouvelle variable en utilisant le constructeur par copie, la référence immutable utilise l’adresse de la variable pour pouvoir y accéder mais sans pouvoir la modifier.

## Quand utiliser quoi ?
Si la variable a une taille plus petite ou à peu près semblable à celle d’un pointeur et qu’il est trivialement copiable (pas d’allocation de mémoire ou de traitements lourd), on préféra utiliser une copie, car la syntaxe est plus simple à lire, que c’est tout aussi rapide en temps d’exécution et qu’il n’y a pas besoin de faire attention à la portée de la variable utilisée.

Cependant, si la variable n’est pas trivialement copiable (un __std::vector__ ou un ____std::string), si elle n’est simplement pas copiable (__std::fstream__) ou bien si sa taille est supérieur à celle d’un pointeur, il vaudra mieux utiliser une référence immutable.

Note : En revanche, avec les références immutables, il faut faire attention à ne pas retourner de référence d’une variable locale, car la référence vers cette variable ne sera pas valide au-delà de la portée de la variable (généralement la fin de la fonction). Normalement, votre compilateur devrait vous prévenir si jamais vous tentez de le faire.

Exemple :
```C++
const std::string& do_not_do_this()
{
    std::string str = "do not reproduce this at home, it is dangerous";
    return str;
}
```
[Source](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-dangle)

_**Note**_ : Dans les boucles for parcourant des conteneurs (comme un std::vector), les mêmes règles s’appliquent.
```C++
std::vector<std::string> strings;

for (const std::string& str: strings)
{
    // do stuff
}

std::vector<int> integers;
for (int i: integers)
{
    // do stuff
}
```

## Quelques cas particuliers
### Multithread 
Si jamais votre variable est utilisée dans plusieurs threads et que l’un des threads est susceptible de modifier la variable, utiliser une copie peut être une bonne idée au lieu de devoir utiliser un mutex à chaque fois que l’on veut accéder à la variable.

### Les pointeurs intelligents (smart pointer)
Si vous voulez utiliser une variable contenue dans un pointeur intelligent (__std::shared_ptr__ par exemple), il vaut mieux utiliser une référence ou un pointeur immutable vers la variable pointée.
[Source](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-smart)
  
### Les templates
En général, pour les _templates_, il vaut mieux utiliser une référence immutable à moins d’être sur que le type utilisé sera trivialement copiable et assez petit.
  
## Récapitulatif des types les plus courants  
  
* Nombre: Copie
* Énumération: Copie
* Structure/Classe: Référence immutable
* Chaine de caractère: Référence immutable
* Conteneur: Référence immutable
* Template: Référence immutable
* Stream: Référence immutable

Il est important de noter que cela reste des conseils généraux et non des règles strictes.