[dcl.attr.noreturn] podaje następujący przykład:
[[ noreturn ]] void f() {
throw "error";
// OK
}
ale nie rozumiem, o co chodzi [[noreturn]]
, ponieważ typ zwracanej funkcji już jest void
.
Jaki jest sens tego noreturn
atrybutu? Jak ma być używany?
c++
c++11
attributes
noreturn
BЈовић
źródło
źródło
Odpowiedzi:
Atrybut noreturn powinien być używany dla funkcji, które nie wracają do programu wywołującego. Nie oznacza to nieważnych funkcji (które zwracają się do wywołującego - po prostu nie zwracają wartości), ale funkcje, w których przepływ sterowania nie powróci do funkcji wywołującej po zakończeniu funkcji (np. Funkcje, które wychodzą z aplikacji, zapętlaj wiecznie lub zgłaszaj wyjątki jak w twoim przykładzie).
Może to być wykorzystane przez kompilatory do optymalizacji i generowania lepszych ostrzeżeń. Na przykład, jeśli
f
ma atrybut noreturn, kompilator może ostrzegać cięg()
przed martwym kodem podczas pisaniaf(); g();
. Podobnie kompilator będzie wiedział, aby nie ostrzegać o brakujących instrukcjach zwrotnych po wywołaniach dof()
.źródło
execve
ta nie powinna powrócić, ale mogłaby ? Czy powinien mieć atrybut noreturn ?noreturn
atrybutu.noreturn
może być użyty tylko wtedy, gdy twoja funkcja ma gwarancję zrobienia czegoś, co kończy działanie programu, zanim przepływ sterowania może powrócić do programu wywołującego - na przykład dlatego, że wywołujesz exit (), abort (), assert (0) itp.noreturn
. Obsługa tego wyjątku nie jest tym samym, co jego zwrócenie. Jakikolwiek kodtry
po wywołaniu jest nadal nieosiągalny, a jeśli nie,void
wówczas żadne przypisanie lub użycie wartości zwracanej nie nastąpi.noreturn
nie mówi kompilatorowi, że funkcja nie zwraca żadnej wartości. Mówi kompilatorowi, że przepływ kontrolny nie powróci do dzwoniącego . Umożliwia to kompilatorowi dokonywanie różnych optymalizacji - nie musi zapisywać i przywracać żadnego niestabilnego stanu wokół połączenia, może kodować martwy kod, eliminując kod, który w przeciwnym razie byłby zgodny z połączeniem itp.źródło
Oznacza to, że funkcja się nie zakończy. Przepływ sterowania nigdy nie uderzy w instrukcję po wywołaniu
f()
:Informacje mogą być wykorzystywane przez kompilator / optymalizator na różne sposoby. Kompilator może dodać ostrzeżenie, że powyższy kod jest nieosiągalny i może modyfikować rzeczywisty kod
g()
na różne sposoby, na przykład w celu obsługi kontynuacji.źródło
-Wno-return
a otrzymasz ostrzeżenie. Prawdopodobnie nie ten, którego się spodziewałeś, ale prawdopodobnie wystarczy powiedzieć, że kompilator ma wiedzę na temat tego, co[[noreturn]]
jest i może z tego skorzystać. (Jestem trochę zaskoczony, że-Wunreachable-code
nie udało mi się ...)-Wmissing-noreturn
, ostrzeżenie oznacza, że analiza przepływu ustaliła, żestd::cout
nie można go osiągnąć. Nie mam pod ręką wystarczająco nowej gcc, aby spojrzeć na wygenerowany zestaw, ale nie byłbym zaskoczony, gdyby połączenieoperator<<
zostało przerwane-O1
już usunąć ten nieosiągalny kod bez[[noreturn]]
podpowiedzi.[[noreturn]]
z kodu. Gdyby ta jednostka tłumacząca miała tylko deklarację funkcji zdefiniowanej gdzie indziej, kompilator nie byłby w stanie usunąć tego kodu, ponieważ nie wie, że funkcja nie zwraca. Właśnie tam atrybut powinien pomóc kompilatorowi.Poprzednie odpowiedzi poprawnie wyjaśniały, czym jest noreturn, ale nie dlaczego . Nie sądzę, aby komentarze dotyczące „optymalizacji” były głównym celem: funkcje, które nie zwracają się, są rzadkie i zwykle nie wymagają optymalizacji. Myślę raczej, że główną racją bytu noreturn jest unikanie fałszywie pozytywnych ostrzeżeń. Rozważmy na przykład ten kod:
Gdyby abort () nie został oznaczony jako „noreturn”, kompilator mógłby ostrzec o tym, że ten kod ma ścieżkę, w której f nie zwraca liczby całkowitej zgodnie z oczekiwaniami. Ale ponieważ abort () jest oznaczony jako brak powrotu, wie, że kod jest poprawny.
źródło
Typ piszący teoretycznie
void
to, co nazywa się w innych językachunit
lubtop
. Jego logicznym odpowiednikiem jest Prawda . Każda wartość może być legalnie przypisanavoid
(każdy typ jest podtypemvoid
). Pomyśl o tym jako o zestawie „wszechświata”; nie ma wspólnych operacji dla wszystkich wartości na świecie, więc nie ma prawidłowych operacji na wartości typuvoid
. Mówiąc inaczej, mówiąc, że coś należy do zestawu wszechświata, nie daje żadnych informacji - już to wiesz. Tak więc dźwięk jest następujący:Ale poniższe zadanie nie jest:
[[noreturn]]
Z drugiej strony, nazywa się czasamiempty
,Nothing
,Bottom
lubBot
i jest logicznym odpowiednikiem fałszywy . Nie ma w ogóle żadnych wartości, a wyrażenie tego typu można rzutować na dowolny typ (tj. Jest podtypem). To jest pusty zestaw. Zauważ, że jeśli ktoś powie ci „wartość wyrażenia foo () należy do pustego zestawu”, jest to bardzo pouczające - mówi ci, że to wyrażenie nigdy nie zakończy normalnego wykonania; przerwie, rzuci lub zawiesi się. Jest to dokładne przeciwieństwovoid
.Poniższe nie ma więc sensu (pseudo-C ++, ponieważ
noreturn
nie jest to typ pierwszej klasy C ++)Ale poniższe przypisanie jest całkowicie uzasadnione, ponieważ
throw
kompilator rozumie, że nie zwraca:W idealnym świecie możesz użyć
noreturn
jako wartości zwracanej dlaraise()
powyższej funkcji :Niestety C ++ na to nie pozwala, prawdopodobnie ze względów praktycznych. Zamiast tego daje możliwość użycia
[[ noreturn ]]
atrybutu, który pomaga kierować optymalizacjami kompilatora i ostrzeżeniami.źródło
void
ivoid
nigdy nie ocenia siętrue
czyfalse
albo cokolwiek innego.true
, nie mam na myśli „wartościtrue
typubool
”, ale logiczny sens, patrz korespondencja Curry-Howarda(void)true;
jest całkowicie poprawny, jak sugeruje odpowiedź.void(true)
jest czymś zupełnie innym, składniowo. Jest to próba utworzenia nowego obiektu typuvoid
poprzez wywołanie konstruktoratrue
jako argumentu; nie udaje się to między innymi dlatego, żevoid
nie jest to pierwsza klasa.