Что действительно делает кастинг на `void`?

Часто используемый оператор типа (void)x; позволяет подавлять предупреждения о неиспользуемой переменной x. Но если я попробую компилировать следующее, я получаю некоторые результаты, которые я не совсем понимаю:

int main()
{
    int x;
    (short)x;
    (void)x;
    (int)x;
}

Компилируя это с помощью g++, я получаю следующие предупреждения:

$ g++ test.cpp -Wall -Wextra -o test
test.cpp: In function ‘int main()’:
test.cpp:4:13: warning: statement has no effect [-Wunused-value]
     (short)x;
             ^
test.cpp:6:11: warning: statement has no effect [-Wunused-value]
     (int)x;
           ^

Итак, я пришел к выводу, что кастинг на void сильно отличается от кастинга на любые другие типы, будь то целевой тип, такой же, как decltype(x) или что-то другое. Мое предположение о возможных объяснениях:

  • Это просто соглашение, что (void)x;, но не другие броски будут подавлять предупреждения. Все утверждения одинаково не влияют.
  • Это различие каким-то образом связано с тем, что void x; не является допустимым выражением, а short x; -.

Какие из них, если они есть, являются более правильными? Если нет, то как объяснить разницу в предупреждениях компилятора?

Ответы

Ответ 1

Приведение в действие void используется для подавления предупреждений компилятора. Стандарт говорит в §5.2.9/4, говорит

Любое выражение может быть явно преобразовано в тип "cv void". значение выражения отбрасывается.

Ответ 2

Это утверждение:

(void)x;

Говорит "Игнорировать значение x". Нет такого типа, как void - это отсутствие типа. Так что это очень отличается от этого:

(int)x;

Что говорит: "Обращайтесь с x как с целым числом". Когда полученное целое число игнорируется, вы получаете предупреждение (если оно включено).

Когда вы игнорируете что-то, что ничто, это не считается проблемой GCC - и не без оснований, поскольку casting to void является идиоматическим способом игнорировать переменную явно в C и С++.

Ответ 3

Стандарт не предусматривает создание предупреждения ( "диагностика" в стандартном) для неиспользуемых локальных переменных или параметров функции. Аналогичным образом, он не предусматривает, как такое предупреждение может быть подавлено. Выделение выражения переменной void для подавления этого предупреждения стало идиомой в сообществе C и более поздних версий С++, потому что результат не может использоваться каким-либо образом (кроме, например, (int)x), поэтому маловероятно, что соответствующий код просто отсутствует. Например:.

(int)x;  // maybe you meant f((int)x);
(void)x; // cannot have intended f((void)x);
(void)x; // but remote possibility: f((void*)x);

Лично я считаю, что это соглашение слишком неясное, поэтому я предпочитаю использовать шаблон функции:

template<typename T>
inline void ignore(const T&) {} // e.g. ignore(x);

Идиоматический способ игнорировать параметры функции - это, однако, опустить свое имя (как видно выше). Частое использование, которое я использую для этой функции, - это когда мне нужно указать параметр функции в условно скомпилированном коде, например assert. Я нахожу, например. следующее более разборчивое, чем использование #ifdef NDEBUG:

void rate(bool fantastic)
{
    assert(fantastic);
    ignore(fantastic);
}

Ответ 4

Возможное использование:

auto it = list_.before_begin();
for (auto& entry : list_)
{
    (void)entry; //suppress warning
    ++it;
}

Теперь итератор 'it' указывает на последний элемент