Зачем приводить неиспользуемые возвращаемые значения к void?

112
int fn();

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

Есть ли причина приводить неиспользованное возвращаемое значение к void, или я прав, думая, что это пустая трата времени?

Следовать за:

Что ж, это кажется довольно всеобъемлющим. Я полагаю, это лучше, чем комментировать неиспользованное возвращаемое значение, поскольку самодокументирующий код лучше, чем комментарии. Лично я отключу эти предупреждения, так как это ненужный шум.

Я съем свои слова, если из-за него сбежит жук ...

markh44
источник

Ответы:

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.
Ричард Корден
источник
это единственное место, где я тоже предпочитаю c-стиль. хотя в моем стандарте кодирования я бы добавил (void) к "a + a;" тоже (правильно сокращено, конечно: p)
Йоханнес Шауб - литб,
8
Несколько не по теме, но зачем вам делать «а + а;» как это без использования возвращаемого значения? Мне это действительно кажется злоупотреблением побочными эффектами и запутывает смысл кода.
Роб К.
5
Что ж, тот, который вы будете использовать каждый день, будет «а = а». Это возвращает присвоенный объекту (для всех хорошо работающих классов! :)). Другой пример: os << "Hello World" << std :: endl. Каждый из них возвращает объект «os».
Ричард Корден,
46

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

static_cast<void>(fn());

Что касается компилятора, приведение возвращаемого значения к void не имеет большого значения.

Дэвид Холм
источник
Подавляет ли он предупреждение о неиспользованных возвращаемых значениях?
Пол Томблин,
Нет. @Mykola: с GCC4 вы можете прикрепить атрибут, который говорит, что возвращаемое значение не должно игнорироваться, что вызовет предупреждение.
Дэвид Холм,
2
Я использую g ++, и он выдает предупреждение даже при таком приведении.
klew
2
какой вариант предупреждения вы используете? -Wunused-value не вызывает предупреждение в моем окружении
Николай Голубев,
В VC ++ это предупреждение подавляет
jalf
39

Истинная причина этого восходит к инструменту, используемому в коде C, который называется lint .

Он анализирует код, ищет возможные проблемы и выдает предупреждения и предложения. Если функция вернула значение, которое затем не проверялось, lintвыдаст предупреждение в случае, если это было случайно. Чтобы отключить lintэто предупреждение, вы отправляете вызов (void).

Дэниел Эрвикер
источник
2
Возможно, это началось таким образом, но теперь у большинства инструментов есть другие механизмы для подавления предупреждений, подобных этому. Кроме того, независимо от того, почему это началось, в частности, в области критического для безопасности кода, это стало обычным способом «документировать» намерения разработчиков.
Ричард Корден,
5
GCC выдает предупреждение об этом с помощью -Wall.
greyfade
23

Приведение в voidиспользуется для подавления предупреждений компилятора о неиспользуемых переменных и несохраненных возвращаемых значениях или выражениях.

Стандарт (2003) говорит в §5.2.9 / 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, похоже, не отвечает на преобразование в void
Мэтт
@Matt: Какую версию GCC вы используете? Можете ли вы разместить свой код на ideone.com или stacked-crooked.com (последний лучше).
Nawaz
1
Подразумевает ли стандартная формулировка «отброшенные», что линтер не должен выдавать предупреждение?
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... это интересный вопрос. В настоящее время я не знаю ответа на этот вопрос. Пожалуйста, поделитесь со мной, если вы все равно узнаете об этом.
Nawaz
8

Начиная с C ++ 17 у нас есть [[maybe_unused]]атрибут, который можно использовать вместо voidприведения.

Флорестан
источник
1
Также добавлен C ++ 17, nodiscardкоторый может представлять интерес: stackoverflow.com/a/61675726/895245 Также я думаю, что вы не можете [[maybe_unused]]напрямую обращаться к вызову, который кажется, только к фиктивной переменной, которая принимает возврат вызова? Если это правильно, это немного шатко.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
4

Превращение в пустоту не требует затрат. Это только информация для компилятора, как с этим обращаться.

KLEW
источник
1
Это «бесплатно», если время на его написание и последующее чтение, понимание (а затем игнорирование) кода бесплатно. ИМО, написание (void)требует времени и сил и заставляет задуматься, знал ли автор, как работают выражения.
wallyk
4

Для функциональности вашей программы приведение к void бессмысленно. Я бы также сказал, что вы не должны использовать его, чтобы сигнализировать о чем-то человеку, читающему код, как предлагает ответ Дэвида. Если вы хотите что-то сообщить о своих намерениях, лучше использовать комментарий. Добавление такого приведения будет только выглядеть странно и вызовет вопросы о возможной причине. Просто мое мнение ...

Дани ван дер Меер
источник
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.Лучший комментарий - это отсутствие комментария , когда код объясняет сам себя. И, опять же, позволяет компилятору корректных предупреждений оставаться в процессе.
underscore_d
«Добавление такого слепка будет только выглядеть странно и вызовет вопросы о возможной причине». Я обнаружил один из них и поэтому читаю это. Для моего неподготовленного мозга это показалось загадочным злоупотреблением стеком.
mynameisnafe
1

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

Colin
источник
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 также позволяет добавлять причину в nodiscardas, [[nodiscard("reason")]]как указано по адресу: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

НКУ 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.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник