Будет имя переменной; Оператор С++ всегда не работает?

В C++ иногда переменная определяется, но не используется. Вот пример — функция для использования с COM_INTERFACE_ENTRY_FUNC_BLIND. ATL-макрос:

HRESULT WINAPI blindQuery( void* /*currentObject*/, REFIID iid, void** ppv, DWORD_PTR /*param*/ ) 
{
    DEBUG_LOG( __FUNCTION__ ); //DEBUG_LOG macro expands to an empty string in non-debug
    DEBUG_LOG( iid );
    iid; // <<<<<<<----silence compiler warning
    if( ppv == 0 ) {
        return E_POINTER;
    }
    *ppv = 0;
    return E_NOINTERFACE;
}

В приведенном выше примере параметр iid используется с макросом DEBUG_LOG, который заменяется пустой строкой в ​​неотладочных конфигурациях. Так что закомментировать или удалить имя переменной iid в подписи не вариант. Когда компилируются неотладочные конфигурации, компилятор выдает предупреждение C4100: 'iid' : unreferenced formal parameter, поэтому, чтобы заглушить предупреждение, добавляется оператор iid;, который считается неоперативным.

Вопрос в следующем: если у нас есть какие-либо из следующих объявлений:

 CSomeType variableName; //or
 CSomeType& variableName; //or
 CSomeType* variableName;

будет следующий оператор в коде C++:

variableName;

быть бездействующим всегда, независимо от того, что такое CSomeType?


person sharptooth    schedule 27.10.2010    source источник
comment
Разве это не вызовет предупреждение об отсутствии эффекта?   -  person falstro    schedule 27.10.2010
comment
@roe: этого нет в Visual C++. Возможно, это происходит на некоторых других компиляторах.   -  person sharptooth    schedule 27.10.2010
comment
Если между объявлением и оператором вы злоупотребляете препроцессором, это может иметь эффект. Например #define variableName exit(-1)   -  person Benoit    schedule 27.10.2010
comment
Также вы не можете объявить ссылку без инициализации. Таким образом, вы получите ошибку C2530: 'variableName': ссылки должны быть инициализированы ошибкой. Однако вы можете использовать макрос UNREFERENCED_PARAMETER, который определен в winnt.h.   -  person gtikok    schedule 27.10.2010
comment
@Benoit: на тот момент это уже не имя переменной, да.   -  person MSalters    schedule 27.10.2010
comment
Глядя на параметры, вы также можете просто обернуть объявление в макрос. Это решает проблему ..., REFIID ONLY_DEBUG(iid), ... Затем в режиме отладки вы можете опустить имя параметра.   -  person edA-qa mort-ora-y    schedule 29.10.2010
comment
да, gcc выдаст предупреждение "утверждение не имеет эффекта"   -  person justin    schedule 01.02.2011


Ответы (2)


Да, но вы, скорее всего, получите еще одно предупреждение.

Стандартный способ сделать это: (void)iid;.


С технической точки зрения это все равно могло бы загрузить iid в регистр и ничего не сделать. Конечно, это очень глупо со стороны компилятора (я сомневаюсь, что кто-либо когда-либо сделает это, если он удалит компилятор), но это более серьезная проблема, если игнорируемое выражение относится к наблюдаемому поведению, например вызовы функций ввода-вывода или чтение и запись volatile переменных.

Возникает интересный вопрос: можем ли мы взять выражение и полностью его игнорировать?

То есть то, что мы имеем сейчас, это:

#define USE(x) (void)(x)

// use iid in an expression to get rid of warning, but have no observable effect
USE(iid); 

// hm, result of expression is gone but expression is still evaluated
USE(std::cout << "hmmm" << std::endl);

Это близко к решению:

// sizeof doesn't evaluate the expression
#define USE(x) (void)(sizeof(x))

Но терпит неудачу с:

void foo();

// oops, cannot take sizeof void
USE(foo());

Решение состоит в том, чтобы просто:

// use expression as sub-expression,
// then make type of full expression int, discard result
#define USE(x) (void)(sizeof((x), 0))

Что гарантирует отсутствие операции.

Редактировать: Вышеупомянутое действительно не гарантировало никакого эффекта, но я опубликовал без тестирования. При тестировании он снова генерирует предупреждение, по крайней мере, в MSVC 2010, поскольку значение не используется. Это нехорошо, время для новых трюков!


Напоминание: мы хотим «использовать» выражение без его вычисления. Как это может быть сделано? Нравится:

#define USE(x) ((void)(true ? 0 : (x)))

У этого есть простая проблема, как и в прошлый раз (на самом деле хуже), в том, что (x) нужно конвертировать в int. Это, опять же, тривиально исправить:

#define USE(x) ((void)(true ? 0 : ((x), 0)))

И мы вернулись к тому же эффекту, что и в прошлый раз (никакого), но на этот раз x "используется", поэтому мы не получаем никаких предупреждений. Готово, верно?

На самом деле есть еще одна проблема с этим решением (и она присутствовала и в последнем не-решении, но осталась незамеченной), и она возникает в этом примере:

struct foo {};
void operator,(const foo&, int) {}

foo f;
USE(f); // oops, void isn't convertible to int!

То есть, если тип выражения (x) перегружает оператор запятой чем-то, что не может быть преобразовано в int, решение терпит неудачу. Конечно, маловероятно, но ради полного перебора мы можем исправить это с помощью:

#define USE(x) ((void)(true ? 0 : ((x), void(), 0)))

Чтобы убедиться, что мы действительно получаем ноль. Этот трюк предложил вам Йоханнес.


Также, как уже отмечалось, если вышесказанного недостаточно, достаточно глупый компилятор потенциально может «загрузить» выражение 0 (в регистр или что-то еще), а затем проигнорировать его.

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

person GManNickG    schedule 27.10.2010
comment
Хотя я согласен, это не означает, что выражение должно стать запретным. При желании компилятор может загрузить значение в регистр, а затем проигнорировать его. Хотя это было бы глупо, не исключено, что оптимизирующий компилятор иногда оставляет биты. - person edA-qa mort-ora-y; 27.10.2010
comment
@eda: Ничего не нужно делать. См. цитату здесь. - person GManNickG; 27.10.2010
comment
Нет, значение выражения отбрасывается. Загрузка значения в регистр или иное указание ЦП на его существование не нарушит это правило. Что касается языка, то он не имел заметных побочных эффектов. - person edA-qa mort-ora-y; 28.10.2010
comment
@edA: но это было бы пустой тратой времени, и компилятору пришлось бы не просто избегать оптимизации, ему пришлось бы активно деоптимизировать код. - person jalf; 28.10.2010
comment
Я не говорю, что хороший оптимизатор сделал бы это, и я не знаю случая, когда бы он это сделал, но я говорю, что автор оптимизатора мог ошибиться - такое случается. Так что если абсолютно критично, чтобы код не генерировался, то его вообще не должно быть. - person edA-qa mort-ora-y; 28.10.2010
comment
@edA: Что насчет последнего определения USE? - person GManNickG; 28.10.2010
comment
Опять же, я не вижу в стандарте ничего, что мешало бы оценке 0. Но что более важно, в этом случае будет оцениваться (x), а не то, что мы хотим. Чтобы решить исходную проблему, мы, вероятно, должны поместить исходный параметр в макрос и добавить неиспользуемый параметр (это работает для GCC, у других компиляторов должна быть альтернатива)... - person edA-qa mort-ora-y; 29.10.2010
comment
@edA: А? Все это находится внутри sizeof, который не оценивает свои аргументы. Конечно, я думаю, что он мог бы загрузить постоянный результат в регистр и снова проигнорировать его. - person GManNickG; 29.10.2010
comment
Ты прав. sizeof не должен оценивать выражение - оно разрешается только в постоянную переменную времени. - person edA-qa mort-ora-y; 29.10.2010
comment
Я получаю предупреждение о том, что левый операнд запятой не влияет на GCC. Любые идеи? - person Steven Lu; 02.06.2011
comment
@Стивен: Хм, нет. Я бы на самом деле не пытался использовать макрос, это был скорее ответ «как далеко-слишком далеко». - person GManNickG; 02.06.2011
comment
Хорошо. Я просто использую это: #define UNUSED(x) ((void)(sizeof(x))) - person Steven Lu; 03.06.2011
comment
@Steven Lu: Хорошо, но я думаю, что это предупреждает о MSVC. Во-первых, лучше не иметь переменной. - person GManNickG; 04.06.2011
comment
@GMan Тогда я мог бы использовать условное выражение для проверки MSVC. Мне все равно нравится придерживаться gcc. Спасибо. - person Steven Lu; 06.06.2011
comment
Это решение выдает предупреждение в g++ 4.9.2. - person Zingam; 17.01.2015
comment
Решение от 2017 года дает warning: left operand of comma operator has no effect [-Wunused-value] на GCC 5.4.0, 5.5.0 и 9.2.1, warning: expression result unused [-Wunused-value] на XCode 11.3.1 и отлично работает на MSVC 19.16.27038.0. - person Laboratorio Cobotica; 17.04.2020

Ну, на самом деле невозможно сказать со 100% уверенностью, не глядя на исходный код компилятора, но я был бы очень удивлен, если бы это когда-либо генерировало какой-либо код в современных компиляторах.

В конце концов, если вас беспокоит какой-то конкретный экземпляр, вы всегда можете посмотреть сгенерированный код сборки.

person Peter Alexander    schedule 27.10.2010