Будет ли "variableName;"C ++ оператор будет неактивным во все времена? - PullRequest
20 голосов
/ 27 октября 2010

В 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 есть?

Ответы [ 2 ]

47 голосов
/ 27 октября 2010

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

Стандартный способ сделать это: (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 (в регистр или что-то в этом роде), а затем проигнорировать его.

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

2 голосов
/ 27 октября 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...