Czy dobrym pomysłem jest zapewnienie różnych sygnatur funkcji, które robią to samo?

23

Oto klasa C ++, która składa się z trzech wartości.

class Foo{

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

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

Wszystkie typy parametrów są różne.
Mógłbym przeciążyć konstruktora, aby kolejność nie miała znaczenia.

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;
};

Ale czy to dobry pomysł?
Zacząłem to robić, ponieważ wiedziałem, czego potrzebuje klasa / funkcja;
Nie zawsze pamiętałem, w jakiej kolejności je przyjęli.


Zakładam, że kompilator optymalizuje to tak, jakbym wywoływał tego samego konstruktora.

//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");

Jakie są Twoje opinie na temat przeciążenia funkcji, aby kolejność nie miała znaczenia?


Ponadto, jeśli piszę jakieś funkcje narzędziowe,
czy dobrym pomysłem jest podanie różnych nazw funkcji, które robią to samo?

na przykład.

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

Często nie widzę tych dwóch, ale podobnych konwencji.
Czy powinienem zerwać z nawykiem?

Trevor Hickey
źródło
1
Wysiłek należy nakładać na wiele sposobów wyrażania czegoś, jeśli każdy wyraźnie określony sposób będzie w niektórych przypadkach wyraźnie i jednoznacznie lepszy od któregokolwiek z pozostałych. Nie oznacza to, że ogólnie należy poświęcić wysiłek, upewniając się, że coś można napisać tylko w jednej kanonicznej formie, ale nie ma sensu wysuwać wysiłku pozwalającego na określenie czegoś w sposób, który nie jest najlepszy.
supercat
Rodzaj duplikatu mojego pytania tutaj (teraz zgadzam się, że to zły pomysł).
leftaroundabout,

Odpowiedzi:

93

Mógłbym przeciążyć konstruktora, aby kolejność [parametrów] nie miała znaczenia ... Ale czy to dobry pomysł?

Nie.

Różne przeciążenia konstruktorów będą miały odwrotny skutek niż zamierzają. Programista, który przyjdzie po tym, że spodziewa się, że różne przeciążenia będą miały inne zachowanie, zapyta: „Jakie odmienne zachowanie jest wyrażane przez każde z tych przeciążeń?

Większość programistów oczekuje dyscypliny posiadania parametrów metody w ustalonej kolejności, a narzędzia takie jak IntelliSense podają im oczekiwaną kolejność parametrów podczas ich wprowadzania.


Posiadanie wielu nazw funkcji, które robią to samo, to ten sam problem; programiści oczekują, że warianty będą miały inne zachowanie. Poproszę jedną funkcję lub metodę na zachowanie i po prostu zastosuj spójny wzorzec nazewnictwa.

Robert Harvey
źródło
14
dużo głosów. Natychmiast przestanę. Dziękuję Ci.
Trevor Hickey
8
Ponadto nie można nazwać konstruktorów inaczej, aby wyjaśnić swój cel, czytelnicy muszą wywnioskować to na podstawie parametrów. Przeciążenie konstruktora w ten sposób znacznie utrudnia zrozumienie.
Zachary Yates
3
„Mając wiele nazw funkcji, które robią to samo ...” - na „dobry” czas, spójrz na klasy Ruby File String Hash Array File .
+1. Masz do czynienia z programistami / programistami. Idea Microsoft „użytkownik jest małpą”, tutaj nie działa.
Manoj R
1
@ZacharyYates Brak „nazw konstruktorów” można obejść, ujawniając zamiast tego metody statyczne. Jest to standardowa praktyka w Javie, choć nie tak bardzo w C ++.
Xion
14

Czasami konieczne jest poparcie komutacji między argumentami. Na przykład:

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

Nie chcemy mnożenia inti doubleobliczać czegoś innego, jeśli operandy są odwrócone. Nie chcemy też zmuszać programistów do zapamiętywania, że ​​mnożąc ints i doubles, doubleidzie po lewej!

Nie ma znaczenia, że ​​jest to operator, ponieważ to samo dotyczy:

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

To zależy od rodzaju funkcji. Podczas gdy większość programistów prawdopodobnie chce obsługi komutatywności w bibliotekach arytmetycznych, niekoniecznie chce, powiedzmy, funkcji biblioteki we / wy do obsługi wszystkich możliwych kolejności argumentów.

Problem prawdopodobnie zależy od oczekiwanego zagnieżdżenia, w które będą zaangażowane wywołania funkcji. Elastyczność operatorów arytmetycznych pozwala nam zmienić ogólną strukturę drzewa składniowego w celu wyjaśnienia ogólnego wyrażenia: zgrupuj podobne terminy i tak dalej.

Kaz
źródło