Est-ce une bonne idée de fournir différentes signatures de fonction qui font la même chose?

23

Voici une classe C ++ qui est construite avec trois valeurs.

class Foo{

    //Constructor
    Foo(std::string, int, char);

private:
    std::string foo;
    char bar;
    int baz;
};

Tous les types de paramètres sont différents.
Je pourrais surcharger le constructeur pour que l'ordre n'ait pas d'importance.

class Foo{

    //Constructors
    Foo(std::string, char, int);
    Foo(std::string, int, char);
    Foo(char, int, std::string);
    Foo(char, std::string, int);
    Foo(int, std::string, char);
    Foo(int, char, std::string);


private:
    std::string foo;
    char bar;
    int baz;
};

Mais est-ce une bonne idée?
J'ai commencé à le faire parce que je savais ce dont une classe / fonction avait besoin;
Je ne me souvenais pas toujours de l'ordre dans lequel cela les a pris.


Je suppose que le compilateur optimise cela comme si j'avais appelé le même constructeur.

//compiler will implement this with the same code? 
//maybe not.. I could call a function to get a parameter, 
//and that function could change the state of the program, before calling
//a function to get another parameter and the compiler would have to
//implement both
Foo foo1("hello",1,'a');
Foo foo2('z',0,"world");

Que pensez-vous de la surcharge d'une fonction pour que l'ordre n'ait pas d'importance?


De plus, si j'écris des fonctions utilitaires,
est-ce une bonne idée de fournir des noms de fonction différents qui font la même chose?

par exemple.

void Do_Foo();
void DoFoo();
void do_foo();
//etc..

Je ne vois pas souvent ces deux conventions mais similaires.
Dois-je rompre ou adopter cette habitude?

Trevor Hickey
la source
1
Il ne faut déployer des efforts pour prévoir de multiples façons d'exprimer quelque chose que si chaque moyen explicitement fourni sera dans certains cas clairement et sans ambiguïté supérieur à l'un des autres. Cela ne signifie pas que l'on devrait généralement déployer des efforts pour s'assurer que quelque chose ne peut être écrit que sous une forme canonique, mais cela n'a pas de sens de déployer des efforts permettant de spécifier quelque chose qui n'est pas le meilleur.
supercat
Une sorte de doublon de ma question ici (je conviens maintenant que c'est une mauvaise idée).
leftaroundabout

Réponses:

93

Je pourrais surcharger le constructeur pour que l'ordre [des paramètres] n'ait pas d'importance ... Mais est-ce une bonne idée?

Non.

Le fait d'avoir des surcharges de constructeur différentes aura l'effet inverse de ce que vous envisagez. Le programmeur qui vient après vous attend que différentes surcharges aient un comportement différent et vous demandera: "Quel type de comportement différent est exprimé par chacune de ces surcharges?

La plupart des programmeurs s'attendent à avoir la discipline d'avoir des paramètres de méthode dans un ordre prédéfini, et des outils comme IntelliSense leur indiqueront l'ordre attendu des paramètres lorsqu'ils les saisiront.


Avoir plusieurs noms de fonction qui font la même chose est le même problème; les programmeurs s'attendent à ce que les variantes aient un comportement différent. Une fonction ou une méthode par comportement, s'il vous plaît, et adoptez simplement un modèle de dénomination cohérent.

Robert Harvey
la source
14
beaucoup de votes positifs. Je m'arrêterai immédiatement. Je vous remercie.
Trevor Hickey
8
De plus, vous ne pouvez pas nommer les constructeurs différemment pour expliquer votre objectif, les lecteurs doivent le déduire des paramètres. Surcharger le constructeur de cette façon le rend beaucoup plus difficile à comprendre.
Zachary Yates
3
"Avoir plusieurs noms de fonctions qui font la même chose ..." - pour un "bon" temps, regardez les classes Ruby String Hash Array File .
+1. Vous traitez avec des développeurs / programmeurs. L'idée de Microsoft «l'utilisateur est un singe» ne fonctionne pas ici.
Manoj R
1
@ZacharyYates Le manque de "noms de constructeurs" peut être contourné en exposant à la place des méthodes de construction statiques. C'est une pratique standard en Java, mais pas tellement en C ++.
Xion
14

Il est parfois nécessaire de soutenir la commutation entre les arguments. Par exemple:

double operator *(int, double);
double operator *(double, int);

Nous ne voulons pas multiplier un intet doublecalculer quelque chose de différent si les opérandes sont inversés. Nous ne voulons pas non plus forcer les programmeurs à se rappeler que lors de la multiplication de ints et doubles, le doubleva à gauche!

Peu importe qu'il s'agisse d'un opérateur car il en va de même pour:

footype plus(const footype &, const bartype &);
footype plus(const bartype &, const footype &);

Cela dépend vraiment des types de fonctions. Alors que la plupart des programmeurs souhaitent probablement la prise en charge de la commutativité dans les bibliothèques arithmétiques, ils ne veulent pas nécessairement, par exemple, des fonctions de bibliothèque d'E / S pour prendre en charge tous les ordres d'arguments possibles.

La préoccupation tourne probablement autour de l'imbrication attendue dans laquelle les appels de fonction vont être impliqués. La flexibilité des opérateurs arithmétiques nous permet de changer la structure globale de l'arbre de syntaxe pour clarifier l'expression globale: regrouper des termes similaires et autres.

Kaz
la source