2015-08-04 10 views
21

Czy można bezpiecznie deklarować następującą funkcję: noexcept, mimo że v.at(idx) teoretycznie może wyrzucić wyjątek out_of_range, ale praktycznie nie z powodu sprawdzenia granic?Czy teoretycznie, ale nie praktycznie, funkcja rzucania nie może być zadeklarowana?

int get_value_or_default(const std::vector<int>& v, size_t idx) noexcept { 
    if (idx >= v.size()) { 
     return -1; 
    } 
    return v.at(idx); 
} 
+0

Plus, ale niekoniecznie jest to bezpieczne. Co się dzieje, gdy inny wątek modyfikuje 'v'? – Bathsheba

+9

@Bathsheba, kod ma niezdefiniowane zachowanie, ponieważ wywołuje 'v.size()' i 'v.at (idx)' bez synchronizacji, więc nie ma sensu martwić się o takie rzeczy. Albo tak się nie stanie, albo program ma większe problemy. –

+2

Ten kod nie jest bezpieczny dla wątków. Wydaje się jednak, że pytanie dotyczy tylko pojedynczego wątku. –

Odpowiedz

15

Jaka jest definicja "bezpiecznego"? Jeśli wyjątek w funkcji oznaczonej jako noexcept lub noexcept(true) wtedy program zostanie zakończony (norma 15.4.9)

Ilekroć jest wyjątek i poszukiwanie obsługi (15,3) napotyka zewnętrzną bloku funkcja z wyjątku specyfikacji nie pozwala wyjątkiem, następnie

  • jeśli wyjątku specyfikacji jest dynamicznie wyjątku specyfikacji, std funkcja :: nieoczekiwane() jest nazwie (15,5. 2),
  • w przeciwnym razie funkcja na std :: terminate() jest wywoływana (15.5.1).

Dopóki jest to dopuszczalne, nic ci nie jest. Jeśli nie możesz tolerować zakończenia programu, nie jest to bezpieczne.

+5

Chodzi o to, że żaden wyjątek nie zostanie odrzucony, nawet jeśli program nie może tolerować zakończenia, to nadal jest bezpieczny, ponieważ nie wywoła std :: terminate(). –

+0

@ JonathanWakely Czy możesz zagwarantować, że wyjątek nigdy nie zostanie odrzucony z tej funkcji? ponieważ funkcja nie jest bezpieczna dla wątków, wektor może być modyfikowany przez inny wątek, a rozmiar zmienia się między kontrolą granic a odzyskiwaniem. – NathanOliver

+5

@NathanOliver: Jak zauważył Jonathan w innym miejscu, jeśli te dostępy są niezsynchronizowane, wtedy masz znacznie większe problemy, które przesłaniają wszystko, co możesz zrobić wewnątrz tej funkcji (poza wprowadzeniem synchronizacji). Takie wysiłki byłyby dosłownie bezcelowe. –

9

Jest bezpieczny. Zgłoszenie funkcji noexcept spowoduje natychmiastowe zakończenie programu (przez wywołanie terminate()) w przypadku wystąpienia wyjątku. Dopóki nie zostanie zgłoszony wyjątek, wszystko jest w porządku.

4

Nie mamy pełnego scenariusza, aby ustalić, czy dokładnie, czy funkcja będzie w stanie rzucić.


Twoje założenia są jednak poprawne: od robisz co std::vector::at staną zrobić, to nieprawdopodobne być szkodliwe w danym kontekście. Jednakże, ogólnie mówiąc, taka funkcja może być przedmiotem czynników unieważniających, a zatem potencjalnie zdolna do zgłaszania wyjątku. Mogą to być wątki, procesy lub sygnały, które mogą przeplatać wykonanie do kodu, który może modyfikować std::vector, który nie może obsłużyć tego bezpiecznie i nie może też std::vector::at.

Jeśli chcesz brać pod uwagę możliwości, jak również, musisz mieć coś podobnego

int get_value_or_default(const std::vector<int>& v, size_t idx) noexcept  
{ 
    try { return v.at(idx); } 
    catch (std::out_of_range const& e) { return -1; } 
} 
+4

Jak zauważył Jonathan w innym miejscu, jeśli te dostępy są niezsynchronizowane, wtedy masz znacznie większe problemy, które przyćmiewają wszystko, co możesz zrobić wewnątrz tej funkcji (poza wprowadzeniem synchronizacji). Takie wysiłki byłyby dosłownie bezcelowe. –

+1

@LightnessRacesinOrbit To prawda, ale ściśle zależy od ogólnego i rzeczywistego kodu. Nie może być asynchronicznego dostępu, który zlikwidowałby takie obawy. Próbowałem skupić się na "niedokładności" kodu, ponieważ jest to główny punkt pytania, co może zrobić asynchroniczne efekty. – edmz

+1

Dobrze, i biorąc pod uwagę, że nie ma problemu z 'noexcept'. –

2

chociaż v.at(idx) teoretycznie może rzucić out_of_range wyjątek, ale praktycznie nie ze względu na granice sprawdzić?

Teoretycznie nie może.

Jeśli myślisz teoretycznie o tej funkcji, to sprawdzanie granic jest częścią tego, co musisz wziąć pod uwagę w teorii tej funkcji jako całości, a więc jedynym teoretycznym sposobem, w jaki można ją rzucić, jest sytuacja, w której wystąpił warunek. w którym idx >= v.size() nie było prawdziwe, a jednak v.at(idx) rzucił.

Tak więc w twoim oświadczeniu, że "teoretycznie może wyrzucić" jest źle.

(W praktyce może to być rzut, jeśli masz błąd w implementacji at() w użyciu, ale wtedy masz większe problemy i wysadzenie w powietrze, ponieważ noexcept może doprowadzić cię do tego, by zrobić to prawdopodobnie na lepsze).

0

Jest możliwe, że ktoś z organizacji big-O wykonał sprytne i wyprowadził swoją klasę z std::vector<int>, a następnie przeładował oba size() i at(), aby rzucić je do swojego gustu. Wtedy gdzieś w kodzie wywoływana jest twoja funkcja, ale ku zaskoczeniu wszystkich program kończy się, a wyjątek jest złapany wyżej.

Rzeczywiście zaistniałe, ale to nie jest kwestia tylko o std::vector<int> ..., ale o praktyce programistycznej w tyle.

Niestety, przyjmujesz założenia dotyczące danych wejściowych, których nie można zagwarantować, i dlatego jest to niebezpieczne; przynajmniej w dużych domenach kodu.

Powiązane problemy