2010-05-12 18 views
21

Mam problem z opcjonalnego parametru funkcji w C++Przechodząc opcjonalny parametr przez odniesienie w C++

Co staram się zrobić to funkcję z opcjonalnym parametrem, który jest przekazywany przez odniesienie pisać, tak że mogę używaj go na dwa sposoby (1) i (2), ale na (2) nie obchodzi mnie, jaka jest wartość mFoobar.

Próbowałem taki kod:

void foo(double &bar, double &foobar = NULL) 
{ 
    bar = 100; 
    foobar = 150; 
} 

int main() 
{ 
    double mBar(0),mFoobar(0); 

    foo(mBar,mFoobar);    // (1) 
    cout << mBar << mFoobar; 

    mBar = 0; 
    mFoobar = 0; 

    foo(mBar);      // (2) 
    cout << mBar << mFoobar; 

    return 0; 
} 

ale wywala na

void foo(double &bar, double &foobar = NULL) 

z komunikatem:

error: default argument for 'double& foobar' has type 'int' 

Czy to możliwe, aby go rozwiązać bez przeciążania funkcji?

+3

Oczywiście, że nie - nawet jeśli * * mógłby przekazać odwołanie NULL, co myślisz, że stanie się to w funkcji, gdy spróbujesz przypisać jej wartość? –

+9

Błąd kompilacji nie jest awarią. – kennytm

+2

Twój kod nie "zawiesza się", jest wyświetlany błąd kompilacji. Awaria może wystąpić dopiero po całkowitym zbudowaniu programu i podjęciu próby jego uruchomienia. –

Odpowiedz

19

Domyślnym argumentem odniesienia (zmiennego) musi być wartość l. Najlepsze, co mogę myśleć, bez przeciążania, jest

+1

Może być warte zdefiniowanie lwartości dla pytającego. (Zasadniczo i nieformalnie "coś, co można przypisać.") – pkh

+2

@pkh Ta definicja jest powszechna, ale błędna. Niektóre wartości r mogą być również przypisane, na przykład 'std :: string (" hello ") = world', a niektóre wartości l nie mogą być przypisane, na przykład C-tablice i obiekty bez operatora przypisania. (Oczywiście, nie można oczywiście przypisać stałych wartości, ale wydaje się to być dobrze zrozumiałe.) – fredoverflow

+4

Może to wprowadzić delikatne problemy z bezpieczeństwem wątków, jeśli obiekt typu dummy zostanie kiedykolwiek odczytany. – Potatoswatter

25

Dlaczego nie możesz używać funkcji przeciążania? Z pewnością jest to najprostsze rozwiązanie twojego problemu?

void foo(double &bar, double &foobar) 
{ 
    bar = 100; 
    foobar = 150; 
} 

void foo(double &bar) 
{ 
    double foobar = 0.0; 
    foo(bar, foobar); 
} 
+12

Mogę, ale chciałem tylko wiedzieć, czy możliwe jest rozwiązanie problemu bez przeciążania. Wyobraź sobie, że budzisz się na wyspie, na której ludzie naprawdę nienawidzą przeładowywania :). – Moomin

+13

@Moomin Przypuszczam, że wyspa "nienawidzę domyślnych parametrów" ma o wiele więcej mieszkańców ;-) – fredoverflow

+0

Funkcja przeciążania powinna być lepszą alternatywą dla nieaktywnego bool w klasie, zwłaszcza gdy istnieją inne klasy dziedziczące z klasy bazowej . – deddebme

29

Nie używaj referencji dla opcjonalnych parametrów. Nie ma koncepcji odniesienia NULL: referencja jest zawsze aliasem do konkretnego obiektu.

Być może spójrz na boost::optional lub std::experimental::optional. boost::optional jest nawet wyspecjalizowany dla typów referencyjnych!

void foo(double &bar, optional<double &> foobar = optional<double &>()) 
-4

Można zrobić ten szalony sposób:

void foo(double &bar, double &foobar = (*(new double()))) 

PS: - Wiem, że to nie jest przyjemne, ale jest po drodze. Należy również pamiętać, aby nie pozostawiać wycieków pamięci! :))

+1

Nawet gdyby nie powodowało niezdefiniowanego zachowania, jaki byłby możliwy mechanizm bezpiecznego usuwania opcjonalnego parametru w przypadkach, w których był on rzeczywiście używany? –

+3

sprawiają, że smart_ptr i jesteś na! – Inverse

+1

Nie ma sposobu na uniknięcie nieszczelności - nie można określić funkcji, czy pamięć została przydzielona za pomocą domyślnego argumentu, czy nie, a poza funkcją nie można znać wskaźnika. Jesteś spuszczony (i przecieka)! –

4

Innym sposobem na to jest użycie wskaźników zamiast referencji. Zapewnia to pożądaną semantykę bez przeciążania. (Osobiście, pewnie bym go z przeciążenia.)

void foo(double* bar, double* foobar = 0) 
{ 
    if (bar) *bar = 100; 
    if (foobar) *foobar = 150; 
} 

    // ... 

    foo(&mBar, &mFoobar); 

    // ... 

    foo(&mBar); 

    // ... 
+2

co prowadzi do kupy! = || == nullptr posypane po całym wszechświecie i potężny ból głowy + opóźnienie w rozpoczęciu pracy, gdy programista czytający twoją 20.000 liniową bibliotekę z nullptr i ** * * ** ** posypany wszędzie nie może dowiedzieć się kto jest właścicielem czego, co należy określić, a co nie, i ma łagodny udar. –

+2

Słyszę, co mówisz, ale sprzedajesz je więcej niż trochę.Jak już wspomniałem, zazwyczaj preferuję używanie przeciążania, ale często wzywam te publiczne, oparte na referencjach, przeładowane funkcje do wspólnej prywatnej funkcji do wykonania rzeczywistej pracy, z czego jestem zadowolony, że przekazuję wskaźniki i wykonuję moje zerowe kontrole. in. – pkh

2

Oto kolejny zwariowany sposób, który nie powoduje wycieki pamięci, które nigdy nie powinno się używać w prawdziwym życiu, ale wydaje się być standardem zgodny na pierwszy rzut oka i zestawia z Visual C++ 2008 & g ++ 3.4.4 pod Cygwin:

void foo(double &bar, double &foobar = (*((double*)0))) 
{ 
    bar = 100; 
    double* pfoobar = &foobar; 
    if (pfoobar != 0) 
     foobar = 150; 
} 

Powtórzmy: NIE ROBIĆ TO! TAM JEST LEPSZE OPCJE! PRZETWARZANIE MOŻE BYĆ PRZYJACIELEM! Ale tak, możesz to zrobić, jeśli jesteś głupi i ostrożny. :)

+1

Odwołania muszą wskazywać obiekt w punkcie wiążącym [ref] (http://stackoverflow.com/questions/4364536/c-null-reference) –

+0

to kompiluje, może ... ale co to robi? próbowałeś go uruchomić? jest to ** nie ** "zgodne ze standardem", więc kompilator może traktować go jako UB i robić absolutnie wszystko - na przykład usunąć rzecz, która ma się zdarzyć, jeśli nie jest NULL, lub całe wywołanie funkcji, lub wysadzenie twojego tostera. kogo to obchodzi? –

+0

Jeden, tak, spróbowałem go uruchomić. Nawiązałem do tego, odnosząc się do dwóch oddzielnych kompilatorów powyżej. Po drugie, wydaje mi się, że jestem mepppa, grając trochę szybko i luźno z koncepcją "przestrzegania standardów" prawie pięć lat temu. W żadnym momencie nie zacytowałem rozdziału i wersetu iw żadnym momencie nie powiedziałem, że było to określone zachowanie. Po trzecie, wielokrotnie powtarzałem, że istnieją lepsze sposoby. Być może nie widzisz tekstu powyżej i poniżej przykładowego kodu, który opublikowałem. Myślę, że pierwotny punkt był dość jasny, że istnieje wiele złych rzeczy, które można zrobić syntaktycznie, ale nie należy tego robić. – CasaDeRobison

1

Znacznie łatwiej jest użyć typu wskaźnika i ustawić go na wartość NULL niż ustawienie wartości domyślnej/opcjonalnej dla parametru odniesienia.

+0

Jest to znacznie łatwiejsze, gdy piszesz swój program. To zagmatwany bałagan "co jest zdefiniowane, kto go zdefiniował, czy ktoś go zdefiniował, a następnie zachował lub przekazał własność, czy muszę nim zarządzać?" dla każdego, kto próbuje go użyć. –

+1

Z drugiej strony odniesienia mogą być mylące: "czy ta funkcja biblioteczna zmieni moją zmienną, czy nie?" co może prowadzić między innymi do problemów związanych z bezpieczeństwem wątków. To kolejny przypadek C/C++ pozostawiając cię w łodzi "oceniaj na swoją szczególną sytuację". Oba mogą być przydatnymi narzędziami w przyborniku. –

+1

@Bryan Shaw: To dlatego masz odniesienia do 'const'. Dopóki funkcja biblioteczna nie przyjmuje "const", powinieneś założyć, że zmieni ona twoją zmienną. W większości przypadków odniesienia powinny być preferowane w stosunku do wskaźników. – Sameer

0

Mówiąc w kategoriach Object Oriented paradygmatu: Jeżeli dana klasa ma i „Default”, to domyślna musi odpowiednio zadeklarowane, a następnie mogą być wykorzystane jako „Default parametr” Ex:

class Pagination { 
private: 
    int currentPage; 
public: 

    //... 
    Pagination() { 
     currentPage = 1; 
     //... 
    } 

    // your Default Pagination (Must be initialized before thread concurrency) 
    static Pagination& Default() { 
     static Pagination p; 
     return p; 
    } 
}; 

na własną metoda ...

 //... 
    std::vector<User> 
    findByFilter(User& audit, Pagination& p = Pagination::Default()) { 
    // ... 

Zmieniano: to rozwiązanie jest całkiem odpowiednie, ponieważ w tym przypadku jest to „globalna default” Paginacja i pojedyncza wartość „odniesienia”. Będziesz mieć również uprawnienia do zmiany wartości domyślnych, takich jak nawigacja/preferencje wyświetlania itp

Edit2: ortograficzny i mocowania ...

Powiązane problemy