Po co rzutować nieużywane wartości zwracane na void?

112
int fn();

void whatever()
{
    (void) fn();
}

Czy istnieje jakiś powód, aby rzutować nieużywaną wartość zwracaną na void, czy też mam rację, myśląc, że to kompletna strata czasu?

Zagryźć:

Cóż, wydaje się to dość wyczerpujące. Przypuszczam, że jest to lepsze niż komentowanie nieużywanej wartości zwracanej, ponieważ samodokumentujący kod jest lepszy niż komentarze. Osobiście wyłączę te ostrzeżenia, ponieważ jest to niepotrzebny hałas.

Zjem moje słowa, jeśli z tego powodu ucieknie błąd ...

markh44
źródło

Odpowiedzi:

79

Odpowiedź Davida prawie obejmuje motywację do tego, aby wyraźnie pokazać innym „programistom”, że wiesz, że ta funkcja zwraca, ale wyraźnie ją ignorujesz.

Jest to sposób na zapewnienie, że w razie potrzeby kody błędów są zawsze obsługiwane.

Myślę, że w przypadku C ++ jest to prawdopodobnie jedyne miejsce, w którym wolę również używać rzutów w stylu C, ponieważ używanie pełnej notacji rzutowania statycznego wydaje się tutaj przesadą. Na koniec, jeśli przeglądasz standard kodowania lub piszesz go, dobrym pomysłem jest również jawne stwierdzenie, że wywołania przeciążonych operatorów (nie używających notacji wywołań funkcji) również powinny być z tego zwolnione:

class A {};
A operator+(A const &, A const &);

int main () {
  A a;
  a + a;                 // Not a problem
  (void)operator+(a,a);  // Using function call notation - so add the cast.
Richard Corden
źródło
to jedyne miejsce, w którym wolę też rzut w stylu c. chociaż w moim standardzie kodowania dodałbym (void) do "a + a;" też (odpowiednio parened, oczywiście: p)
Johannes Schaub - litb
8
Trochę nie na temat, ale po co miałbyś robić "a + a;" tak bez użycia wartości zwracanej? To naprawdę wydaje mi się nadużywaniem efektów ubocznych i zaciemnia intencję kodu.
Rob K
5
Cóż, ten, którego będziesz używać codziennie, to „a = a”. To zwraca wartość przypisaną do obiektu (dla wszystkich dobrze zachowujących się klas, czyli! :)). Innym przykładem jest: os << "Hello World" << std :: endl. Każdy z nich zwraca obiekt „os”.
Richard Corden
46

W pracy używamy tego do uznania, że ​​funkcja ma wartość zwracaną, ale programista zapewnił, że można ją bezpiecznie zignorować. Ponieważ oznaczyłeś pytanie jako C ++, powinieneś używać static_cast :

static_cast<void>(fn());

Jeśli chodzi o kompilator, rzutowanie zwracanej wartości na void ma niewielkie znaczenie.

David Holm
źródło
Czy pomija ostrzeżenie o nieużywanych wartościach zwracanych?
Paul Tomblin,
Nie. @Mykola: W GCC4 możesz dołączyć atrybut, który mówi, że wartość zwracana nie powinna być ignorowana, co spowoduje ostrzeżenie.
David Holm,
2
Używam g ++ i daje ostrzeżenie nawet przy takiej obsadzie.
klew
2
jakiej opcji ostrzeżenia używasz? -Wartość niewykorzystana nie wywołuje ostrzeżenia w moim otoczeniu
Mykoła Gołubjew
W VC ++ wyłącza ostrzeżenie
jalf
39

Prawdziwy powód takiego działania sięga narzędzia używanego w kodzie C, zwanego lint .

Analizuje kod pod kątem możliwych problemów oraz wydaje ostrzeżenia i sugestie. Gdyby funkcja zwróciła wartość, która nie została następnie sprawdzona, lintostrzegałaby, gdyby było to przypadkowe. Aby uciszyć lintto ostrzeżenie, rzucasz wezwanie do (void).

Daniel Earwicker
źródło
2
Być może zaczęło się w ten sposób, ale większość narzędzi ma teraz inne mechanizmy tłumienia takich ostrzeżeń. Niezależnie od tego, dlaczego to się zaczęło, w szczególności w dziedzinie kodu krytycznego dla bezpieczeństwa, stało się to zwykłym sposobem „dokumentowania” zamiarów programistów.
Richard Corden,
5
GCC daje ostrzeżenie o tym z -Wall.
greyfade
23

Rzutowanie na voidsłuży do pomijania ostrzeżeń kompilatora dla nieużywanych zmiennych i niezapisanych zwracanych wartości lub wyrażeń.

Standard (2003) mówi w §5.2.9 / 4 mówi:

Każde wyrażenie można jawnie przekonwertować na typ „cv void”. Wartość wyrażenia jest odrzucana .

Możesz więc napisać:

//suppressing unused variable warnings
static_cast<void>(unusedVar);
static_cast<const void>(unusedVar);
static_cast<volatile void>(unusedVar);

//suppressing return value warnings
static_cast<void>(fn());
static_cast<const void>(fn());
static_cast<volatile void>(fn());

//suppressing unsaved expressions
static_cast<void>(a + b * 10);
static_cast<const void>( x &&y || z);
static_cast<volatile void>( m | n + fn());

Wszystkie formularze są ważne. Zwykle skracam go jako:

//suppressing  expressions
(void)(unusedVar);
(void)(fn());
(void)(x &&y || z);

To też jest w porządku.

Nawaz
źródło
1
Tyle że nie zawsze wyłącza ostrzeżenia kompilatora. Wygląda na to, że gcc nie reaguje na casting do void
Matt
@Matt: Której wersji GCC używasz? Czy możesz opublikować swój kod na ideone.com lub stacked-crooked.com (ta druga opcja jest lepsza).
Nawaz
1
Czy standardowe sformułowanie „odrzucone” oznacza, że ​​linters nie powinien podnosić ostrzeżenia?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... to jest interesujące pytanie. Obecnie nie znam odpowiedzi na to pytanie. Proszę, podziel się ze mną, jeśli się o tym dowiesz.
Nawaz
8

Od c ++ 17 mamy [[maybe_unused]]atrybut, którego można użyć zamiast voidrzutowania.

florestan
źródło
1
Dodano również C ++ 17, nodiscardktóry może być interesujący: stackoverflow.com/a/61675726/895245 Również myślę, że nie możesz zastosować [[maybe_unused]]bezpośrednio do wywołania, jak się wydaje, tylko do fałszywej zmiennej, która akceptuje zwrot wywołania? Jeśli to prawda, jest trochę niepewne.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
4

Rzucanie w pustkę jest darmowe. Jest to tylko informacja dla kompilatora, jak go traktować.

klew
źródło
1
Jest to „bezkosztowe”, jeśli czas na jego napisanie i późniejsze czytanie, zrozumienie (a następnie ignorowanie) kodu jest wolny. IMO, pisanie (void)kosztuje mnie czas i energię i sprawia, że ​​zastanawiam się, czy autor wiedział, jak działają wyrażenia.
wallyk
4

Ponieważ funkcja rzutowania programu na void jest bez znaczenia. Twierdzę również, że nie należy go używać do sygnalizowania czegoś osobie, która czyta kod, jak sugeruje David w odpowiedzi. Jeśli chcesz przekazać coś o swoich zamiarach, lepiej użyć komentarza. Dodanie takiej obsady będzie wyglądać dziwnie i wywoła pytania o możliwy powód. Tylko moja opinia...

Dani van der Meer
źródło
2
Jest to znany wzorzec, który wyraźnie mówi innym, że nie obchodzi cię zwracana wartość, że nie zapomniałeś sobie z tym poradzić.
Spidey
For the functionality of you program casting to void is meaninglessJeśli chodzi o samokontrolę i liczbę ostrzeżeń podczas kompilacji, tak naprawdę nie jest. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.Najlepszym komentarzem jest brak komentarza , gdy kod sam się wyjaśnia. I znowu utrzymuje poprawny kompilator ostrzeżeń w tym procesie.
underscore_d
„Dodanie takiej obsady będzie tylko dziwnie wyglądać i rodzić pytania o możliwy powód”. Odkryłem jeden z nich i dlatego to czytam. Wydawało mi się, że mój niewytrenowany mózg to tajemnicze nadużycie stosu.
mynameisnafe
1

Również podczas weryfikacji kodu zgodnego ze standardami MISTA (lub innymi), automatyczne narzędzia, takie jak LDRA, nie pozwolą Ci wywołać funkcji, która ma typ zwracany bez zwracania przez nią wartości, chyba że jawnie rzutujesz zwróconą wartość na (void)

Colin
źródło
0

C ++ 17 [[nodiscard]]

C ++ 17 ustandaryzował „zwracaną wartość biznesową ignorowaną” za pomocą atrybutu.

Dlatego mam nadzieję, że zgodne implementacje zawsze będą ostrzegać tylko wtedy, gdy nodiscardzostaną podane, i nigdy nie będą ostrzegać inaczej.

Przykład:

main.cpp

[[nodiscard]] int f() {
    return 1;
}

int main() {
    f();
}

skompilować:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -o main.out main.cpp

wynik:

main.cpp: In function int main()’:
main.cpp:6:6: warning: ignoring return value of int f()’, declared with attribute nodiscard [-Wunused-result]
    6 |     f();
      |     ~^~
main.cpp:1:19: note: declared here
    1 | [[nodiscard]] int f() {
      | 

Poniższe wszystkie pozwalają uniknąć ostrzeżenia:

(void)f();
[[maybe_unused]] int i = f();

Nie mogłem użyć maybe_unusedbezpośrednio podczas f()rozmowy:

[[maybe_unused]] f();

daje:

main.cpp: In function int main()’:
main.cpp:6:5: warning: attributes at the beginning of statement are ignored [-Wattributes]
    6 |     [[maybe_unused]] f();
      |     ^~~~~~~~~~~~~~~~

Działanie (void)rzutowania nie wydaje się być obowiązkowe, ale standard jest „ zalecany ”: Jak celowo odrzucić wartość zwracaną przez [[nodiscard]]?

Jak widać z komunikatu ostrzegawczego, jednym „rozwiązaniem” ostrzeżenia jest dodanie -Wno-unused-result:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -Wno-unused-result -o main.out main.cpp

chociaż oczywiście nie polecałbym ignorowania takich ostrzeżeń na całym świecie.

C ++ 20 pozwala również dodać powód do nodiscardjak w, [[nodiscard("reason")]]jak wspomniano na: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

warn_unused_resultAtrybut GCC

Przed standaryzacją [[nodiscard]]i dla C zanim ostatecznie zdecydują się na standaryzację atrybutów, GCC zaimplementowało dokładnie tę samą funkcjonalność z warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

co daje:

main.cpp: In function int main()’:
main.cpp:8:6: warning: ignoring return value of int f()’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     f();
      |     ~^~

Należy zatem zauważyć, że ponieważ ANSI C nie ma na to standardu, ANSI C nie określa, które funkcje biblioteki standardowej C mają atrybut lub nie, a zatem implementacje podjęły własne decyzje dotyczące tego, co powinno lub nie być oznaczone warn_unuesd_result, co dlatego generalnie należałoby użyć (void)rzutowania, aby zignorować zwrot wszelkich wywołań funkcji biblioteki standardowej, aby w pełni uniknąć ostrzeżeń w jakiejkolwiek implementacji.

Przetestowano w GCC 9.2.1, Ubuntu 19.10.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło