HOWTO · C++

Comment retourner une chaîne de caractères à partir d'une fonction en C++

Cet article montre comment retourner une chaîne de caractères à partir d'une fonction en C++.

Sur cette page

Pour une fonction C++ ordinaire, renvoyez un std::string par valeur. L’appelant reçoit ainsi une chaîne indépendante, et C++ peut appliquer l’optimisation du retour lorsque c’est possible. Utilisez une référence uniquement pour renvoyer un objet existant dont la durée de vie est gérée ailleurs, et un pointeur ou une chaîne de style C seulement si une interface l’exige. Les exemples ci-dessous utilisent C++11 ou une version ultérieure.

Renvoyer une chaîne par valeur

Le retour par valeur est le choix par défaut le plus clair lorsqu’une fonction crée ou transforme une chaîne. L’appelant reçoit sa propre valeur de chaîne et peut la stocker ou la modifier indépendamment. Renvoyer une variable locale nommée est généralement efficace : le compilateur peut construire le résultat directement à l’emplacement de destination de l’appelant grâce à l’optimisation du retour de valeur nommée. Si cette optimisation ne s’applique pas, le std::string peut être déplacé. Aucun de ces mécanismes ne nécessite d’ajouter std::move à l’instruction de retour.

Exécutez cet exemple. Il affiche « Hello, C++! », texte qui reste en anglais dans l’exemple conservé à l’identique.

#include <iostream>
#include <string>

std::string makeGreeting() {
    std::string result = "Hello, C++!";
    return result;
}

int main() {
    std::string greeting = makeGreeting();
    std::cout << greeting << '\n';
}

Le type de retour de la fonction est std::string, et return result; renvoie la valeur locale. La variable locale cesse d’exister à la fin de la fonction, mais la chaîne renvoyée reste disponible pour l’appelant. Renvoyer une valeur est généralement plus simple et plus sûr que de gérer séparément la propriété d’une chaîne allouée dynamiquement.

Renvoyer une référence à une chaîne existante

Le retour d’une référence désigne un objet déjà existant ; il ne crée pas de nouvelle chaîne. Cette approche peut être utile lorsqu’une fonction sélectionne ou expose une chaîne appartenant à un objet dont la durée de vie est plus longue. L’appelant accède au même objet : les modifications effectuées par l’intermédiaire d’une référence non constante affectent donc l’objet d’origine. Utilisez une const std::string& lorsque l’appelant doit pouvoir lire la chaîne sans la modifier.

L’objet référencé doit rester vivant chaque fois que la référence renvoyée est utilisée. Ne renvoyez jamais une référence à une variable locale : celle-ci est détruite à la sortie de la fonction, et l’appelant se retrouve avec une référence pendante. Le renvoi d’un paramètre de référence n’est sûr que si l’appelant maintient son argument en vie, comme dans cet exemple. Celui-ci affiche « Existing string », texte qui reste en anglais dans l’exemple conservé à l’identique :

#include <iostream>
#include <string>

const std::string& sameString(const std::string& value) {
    return value;
}

int main() {
    std::string original = "Existing string";
    const std::string& alias = sameString(original);
    std::cout << alias << '\n';
}

Renvoyer une chaîne par pointeur

Le retour d’un pointeur peut représenter un accès facultatif ou respecter une interface existante, mais la durée de vie et la propriété de l’objet font alors partie du contrat. Ne renvoyez l’adresse d’une chaîne existante que si celle-ci reste valide pendant toute utilisation du pointeur. Précisez qui est propriétaire de l’objet et si le pointeur peut être nul.

N’allouez pas un std::string avec new dans le seul but de le renvoyer. L’appelant devrait alors penser à le libérer, ce qui complique les retours anticipés et la gestion des erreurs. Pour une chaîne nouvellement créée, préférez le retour par valeur. L’exemple ci-dessous pointe vers un objet existant et le maintient en vie pendant l’utilisation du pointeur. Il affiche « Hello through a pointer », texte qui reste en anglais dans l’exemple conservé à l’identique :

#include <iostream>
#include <string>

const std::string* findGreeting(const std::string& value) {
    return &value;
}

int main() {
    std::string greeting = "Hello through a pointer";
    const std::string* result = findGreeting(greeting);
    if (result != nullptr) {
        std::cout << *result << '\n';
    }
}

La fonction ne prend pas possession de value. L’appelant ne doit pas conserver le pointeur renvoyé après la destruction de la chaîne référencée.

N’utiliser std::move que lorsque cela est utile

std::move est une conversion qui permet une opération de déplacement ; elle ne déplace pas les données à elle seule. Dans une instruction de retour, écrire return std::move(local); est généralement inutile. Cela peut empêcher l’optimisation du retour de valeur nommée et remplacer potentiellement la construction directe par un déplacement. Pour un résultat local, préférez return local; et laissez le langage et le compilateur choisir l’optimisation applicable.

Un déplacement explicite est utile lorsqu’on transfère un objet qui ne peut pas bénéficier du déplacement automatique, par exemple un membre qu’il faut déplacer hors d’un objet dont la durée de vie est plus longue. La source est alors modifiée : ne procédez ainsi que si vous n’avez plus besoin de son état précédent. N’ajoutez pas std::move dans le seul but de donner l’impression que le retour sera plus rapide.

Renvoyer une chaîne de style C avec char*

Une chaîne de style C est une séquence de caractères terminée par un caractère nul, et non un std::string. Si une API exige un pointeur vers des caractères et que le résultat est un texte fixe, renvoyez un const char* qui pointe vers un littéral de chaîne. Les littéraux de chaîne ont une durée de stockage statique : le pointeur reste donc valide pendant toute l’exécution du programme. Ici, const char* désigne un pointeur vers des caractères const : ceux-ci ne peuvent pas être modifiés par l’intermédiaire du pointeur, mais le pointeur lui-même n’est pas constant et peut être réaffecté. C’est différent de char* const, qui désigne un pointeur constant vers des caractères modifiables. Le littéral de chaîne ne doit pas être modifié. L’exemple affiche « Hello from C-style text », libellé qui reste en anglais dans l’exemple conservé à l’identique.

#include <iostream>

const char* greeting() {
    return "Hello from C-style text";
}

int main() {
    std::cout << greeting() << '\n';
}

Ne renvoyez pas un pointeur vers un tableau de caractères local, qui cesse d’exister au retour de la fonction, ni un pointeur vers un std::string local. Le résultat de c_str() dépend de la durée de vie de l’objet std::string et peut être invalidé par des opérations qui modifient cette chaîne. Si l’appelant a besoin d’un résultat indépendant qu’il peut modifier, renvoyez plutôt un std::string.