Будет ли "variableName"; Оператор С++ всегда не работает?

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

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;, который, как считается, является no-op.

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

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

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

variableName;

быть не-оператором во все времена независимо от того, что CSomeType?

Ответ 1

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

Стандартный способ сделать это: (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))

Это не гарантирует работу. Забастовкa >

Изменить: Вышеупомянутое не гарантировало никакого эффекта, но я отправил его без тестирования. После тестирования он снова генерирует предупреждение, по крайней мере, в 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 (в регистр или что-то еще), а затем проигнорировать его.

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

Ответ 2

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

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