Je me demande s'il est possible de définir une valeur par défaut pour un argument de type const reference dans une fonction en C++. J'ai écrit le code suivant, qui compile sans problème et passe clang-tidy sans erreurs. Cependant, je suis un peu perplexe car je n'ai jamais vu cette pratique auparavant. Est-ce une bonne pratique ? Y a-t-il des inconvénients ou des alternatives recommandées ?
Exemple :
main.cpp
#include "Character.h"
#include <iostream>
void callPrintName(const Character& character = Character("unknown")) {
character.printName();
}
int main() {
Character c("Jak");
callPrintName(c);
callPrintName();
return 0;
}
Character.h
#pragma once
#include <string>
class Character {
public:
Character(const std::string& name);
void printName() const;
private:
std::string name_;
};
PS : Je me pose ce genre de question tordue car je travaille sur un projet où un ancien développeur s'est amusé à mettre plein de fonctions qui passent leurs arguments par valeur mais contiennent aussi des arguments par défaut. Du coup j'aimerais ajouter un passage par référence constante pour améliorer la qualité du code sans casser l'idée de l'argument par défaut qui avait été ajouté.
Hors sujet, ajouter des references n'est pas forcement une amélioration. Dans ton dernier code par exemple, il vaudrait mieux ajouter un std::move sur name.
Utilise clang-tidy et les warnings "modernize" pour analyser ton code.
tu forces le développeur à utiliser std::move() s'il veut passer une variable en argument du constructeur, ce qui l'empêchera d'utiliser sa variable dans les lignes du dessous.
On ne force pas à avoir une rvalue et la mémoire n'est pas copiée, mais bien déplacée dans le membre. Comme dit @gbdivers, ce sont des warnings que remonte clang-tidy.
Dans les faits, avoir un constructeur avec une rvalue et un autre avec const-réf permet d'avoir un code optimal, mais c'est lourd et le nombre de constructeur est exponentiel au nombre de paramètre pour un bénéfice discutable. Du coup, si le paramètre implémente un constructeur de déplacement, il vaut mieux le prendre par valeur et faire un std::move sur l'init du membre. Sur l'appel, si l'utilisateur passe une référence, il n'y a pas de changement, mais s'il donne une rvalue/temporaire il n'y aura qu'une allocation de std::string (plutôt que 2 avec une référence).
De plus, si le type est petit (on va dire plus petit oui égal à 2 * sizeof(void*)), il vaut mieux prendre par valeur plutôt qu'une référence, car le passage de paramètre est plus efficace. C'est pour cela qu'il est conseillé de prendre par valeur les vues comme std::string, std::span, QStringView, etc.
Discord NaN. Mon site.