Чому викидаєте невикористані значення повернення до недійсних?


112
int fn();

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

Чи є якась причина, щоб викинути невикористане повернене значення недійсним, чи я маю рацію, думаючи, що це повна трата часу?

Слідувати:

Добре, що здається досить вичерпним. Я вважаю, що це краще, ніж коментувати невикористане значення повернення, оскільки самодокументування коду краще, ніж коментарі. Особисто я відключу ці попередження, оскільки це зайвий шум.

Я з'їм свої слова, якщо помилка втече через це ...

Відповіді:


79

Девід відповідь досить багато охоплює мотивацію для цього, явно показати інші «розробник» , що ви знаєте , ця функція повертає , але ви явно ігноруючи його.

Це спосіб гарантувати, що там, де необхідні коди помилок, завжди обробляються.

Я думаю, що для C ++ це, мабуть, єдине місце, де я також вважаю за краще використовувати касти в стилі C, оскільки використання повної статичної нотації листування просто відчуває себе надмірною. Нарешті, якщо ви переглядаєте стандарт кодування або пишете його, тоді також непогано чітко вказати, що дзвінки перевантаженим операторам (не використовують функцію нотації викликів) також повинні бути виключені з цього:

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.

це єдине місце, де я теж віддаю перевагу акторському стилю. Тхо в моєму стандарті кодування, я б додав (недійсний) до "a + a;" теж (правильно налаштований, звичайно: p)
Йоганнес Шауб - ліб

8
Дещо поза темою, але чому б ти взагалі робив "a + a;" так, без використання зворотного значення? Це насправді здається зловживанням побічними ефектами для мене і пригнічує наміри коду.
Роб К

5
Що ж, той, який ви будете використовувати щодня, буде "a = a". Це повертає призначений об’єкт (для всіх добре поводиться класів, що є! :)). Інший приклад: os << "Hello World" << std :: endl. Кожен з них повертає об’єкт "os".
Річард Корден

46

На роботі ми використовуємо це для того, щоб визнати, що функція має повернене значення, але розробник стверджував, що її можна ігнорувати. Оскільки ви позначили це питання як C ++, ви повинні використовувати static_cast :

static_cast<void>(fn());

Що стосується того, що компілятор закидає повернене значення до недійсного, мало значення.


Чи придушує це попередження про невикористані значення повернення?
Пол Томблін

Ні, це не є. @Mykola: За допомогою GCC4 ви можете приєднати атрибут, який говорить про те, що значення повернення не слід ігнорувати, що призведе до попередження.
Девід Холм

2
Я використовую g ++, і він попереджає навіть при такому ролі.
klew

2
який варіант попередження ви використовуєте? -Використане значення не викликає попередження в моєму оточенні
Микола Голубєв,

У VC ++ він придушує попередження
джельф

39

Справжня причина цього полягає в інструменті, який використовується на коді С, який називається lint .

Він аналізує код, шукаючи можливі проблеми та видаючи попередження та пропозиції. Якщо функція повернула значення, яке тоді не перевірялося, lintпопередить, якщо це було випадково. Щоб замовкнути lintнад цим попередженням, ви кидаєте заклик (void).


2
Можливо, це почалося саме так, але тепер у більшості інструментів є інші механізми придушення таких попереджень. Також - незалежно від того, чому це почалося, зокрема, в області критичного коду безпеки, це стало звичним способом "документувати" наміри розробника.
Річард Корден

5
GCC подає попередження про це за допомогою -Wall.
greyfade

23

Casting to voidвикористовується для придушення попереджень компілятора щодо невикористаних змінних та незбережених значень повернення або виразів.

Стандарт (2003) говорить у § 5.2.2 / 4,

Будь-який вираз може бути явно перетворений у тип "cv void". Значення виразу відкидається .

Тож ви можете написати:

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

Усі форми дійсні. Я зазвичай роблю це коротше:

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

Це також добре.


1
За винятком того, що це не завжди вимикає попередження компілятора. gcc не здається реагувати на кастинг недійсним
Метт

@Matt: Яку версію GCC ви використовуєте? Чи можете ви розмістити свій код на ideone.com або stacked-crooked.com (останній краще).
Наваз

1
Чи означає, що стандартне "відкинуте" формулювання означає, що попередження не повинно підніматися лінерами?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... це цікаве питання. На даний момент я не знаю відповіді на це. Будь ласка, поділіться зі мною, якщо ви все-таки дізнаєтесь про це.
Наваз

8

Оскільки c ++ 17, у нас є [[maybe_unused]]атрибут, який можна використовувати замість voidакторів.


1
Також додано C ++ 17, nodiscardщо може зацікавити: stackoverflow.com/a/61675726/895245 Також я думаю, що ви не можете застосувати [[maybe_unused]]безпосередньо до виклику, як здається, лише до фіктивної змінної, яка приймає повернення дзвінка? Якщо це правильно, це трохи химерно.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4

Покинути недійсність - це не дорого. Це лише інформація для компілятора, як з цим ставитися.


1
Це "недорого", якщо час на його написання та подальше час читання, розуміння (а потім ігнорування) коду вільне. IMO, написання (void)коштує мені часу та енергії та змушує задуматися, чи автор знав, як працюють вирази.
wallyk

4

Для функціональності вашої програми кастинг не втрачає сенсу. Я також заперечую, що ви не повинні використовувати це для того, щоб щось сигналізувати людині, яка читає код, як це запропоновано у відповіді Девіда. Якщо ви хочете щось повідомити про свої наміри, краще скористатися коментарем. Додавання такого акторського складу буде виглядати лише дивно і викликає питання щодо можливої ​​причини. Просто моя думка ...


2
Це відомий зразок, який дозволяє чітко сказати іншим, що вам не байдуже значення, що повертається, що ви просто не забули обробити його.
Spidey

For the functionality of you program casting to void is meaninglessЩо стосується самодокументації та кількості попереджень при складанні, це насправді не так. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.Найкращий коментар - це не коментар , коли код пояснює себе. І, знову ж таки, зберігає дійсно компілятор з попередженнями.
підкреслюй_d

"Додавання такого акторського складу буде виглядати лише дивно і викликає питання щодо можливої ​​причини". Я виявив одне з них, і саме тому я це читаю. Здавалося, це таємниче зловживання моїм нетренованим мозку.
mynameisnafe

1

Крім того, коли перевірка вашого коду відповідає стандартам MISTA (або іншим), автоматичні інструменти, такі як LDRA, не дозволять вам викликати функцію, що має тип повернення, без того, щоб вона повертала значення, якщо ви явно не передаєте повернене значення (недійсне)


0

C ++ 17 [[nodiscard]]

C ++ 17 стандартизував "повернення значення ігнорованого бізнесу" з атрибутом.

Тому я сподіваюся, що сумісні реалізації завжди будуть попереджати лише тоді, коли nodiscard вони надані, і ніколи не попереджають інакше.

Приклад:

main.cpp

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

int main() {
    f();
}

скласти:

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

результат:

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() {
      | 

Наступне уникайте попередження:

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

Мені не вдалося скористатися maybe_unusedбезпосередньо під час f()виклику:

[[maybe_unused]] f();

дає:

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

Робота (void)не є обов'язковою, але вона "заохочується" у стандарті: Як я навмисно відкидаю [[nodiscard]] повернене значення?

Як видно з попереджувального повідомлення, одним із "рішень" попередження є додати -Wno-unused-result:

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

хоча я, звичайно, не рекомендував би ігнорувати попередження у всьому світі.

C ++ 20 також дозволяє додати причину в nodiscardякості в [[nodiscard("reason")]]якості згадується: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

GCC warn_unused_resultАтрибут

До стандартизації [[nodiscard]]та для C, перш ніж вони нарешті вирішили стандартизувати атрибути, GCC реалізував такий самий функціонал із warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

що дає:

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();
      |     ~^~

Тоді слід зауважити, що оскільки ANSI C не має для цього стандарту, ANSI C не визначає, які стандартні функції бібліотеки C мають атрибут чи ні, і тому реалізація прийняла власні рішення щодо того, що слід чи не слід позначати warn_unuesd_result, яке тому загалом вам доведеться використовувати команду (void)для ігнорування повернень будь-яких викликів до стандартних функцій бібліотеки, щоб повністю уникнути попереджень при будь-якій реалізації.

Випробувано в GCC 9.2.1, Ubuntu 19.10.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.