Подтвердить что ты не робот

Какой переносимый способ реализовать оператор no-op в С++?

Время от времени возникает необходимость в операторе no-op в С++. Например, при реализации assert(), который отключен в конфигурации без отладки (см. Также этот вопрос):

#ifdef _DEBUG
#define assert(x) if( !x ) { \
                     ThrowExcepion(__FILE__, __LINE__);\
                  } else {\
                     //noop here \
                  }
#else
#define assert(x) //noop here
#endif

До сих пор у меня сложилось впечатление, что правильным способом является использование (void)0; для no-op:

(void)0;

однако я подозреваю, что он может вызывать предупреждения для некоторых компиляторов - что-то вроде C4555: expression has no effect; expected expression with side-effect Предупреждение Visual С++, которое не выбрано для данного конкретного случая, но выдается, когда нет нажатия на void.

Универсально ли он переносится? Есть ли лучший способ?

4b9b3361

Ответ 1

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

Вряд ли, так как ((void)0) - это то, что стандартный assert макрос расширяется до тех пор, пока NDEBUG не определен. Поэтому любой компилятор, который выдает предупреждения для него, выдает предупреждения, когда код, содержащий утверждения, компилируется для выпуска. Я ожидаю, что это будет считаться ошибкой пользователей.

Я предполагаю, что компилятор мог бы избежать этой проблемы, предупредив ваше предложение (void)0, рассматривая только ((void)0) специально. Поэтому вам может быть полезно использовать ((void)0), но я сомневаюсь.

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

Обратите внимание, что в С++ стандартным заголовкам разрешено включать друг друга. Поэтому, если вы используете какой-либо стандартный заголовок, assert может быть определен этим. Таким образом, ваш код не переносится в этой учетной записи. Если вы говорите "универсально переносимым", вы обычно должны рассматривать любой макрос, определенный в любом стандартном заголовке, в качестве зарезервированного идентификатора. Вы могли бы определить его, но использование другого имени для ваших собственных утверждений было бы более разумным. Я знаю это только пример, но я не понимаю, почему вы когда-либо хотели бы определить assert в "универсально-переносном" способе, поскольку все реализации С++ уже имеют его, и он не делает то, что вы определяя его здесь.

Ответ 2

Самый простой no-op просто не имеет кода вообще:

#define noop

Тогда код пользователя будет иметь:

if (condition) noop; else do_something();

Альтернативой, о которой вы упоминаете, также является no-op: (void)0;, но если вы собираетесь использовать это внутри макроса, вы должны оставить ; в стороне, чтобы вызывающий мог добавить:

#define noop (void)0
if (condition) noop; else do_something();

(Если ; был частью макроса, тогда там был бы лишний ;)

Ответ 3

Как насчет do { } while(0)? Да, он добавляет код, но я уверен, что большинство компиляторов сегодня могут его оптимизировать.

Ответ 4

; считается стандартным no-op. Обратите внимание, что возможно, что компилятор не будет генерировать код из него.

Ответ 5

AFAIK, он универсально переносится.

#define MYDEFINE()

будет делать также.

Другой вариант может быть примерно таким:

void noop(...) {}
#define MYDEFINE() noop()

Однако я бы придерживался (void)0 или использовал встроенные функции, такие как __ noop

Ответ 6

Я рекомендую использовать:

static_cast<void> (0)   

Ответ 7

этот код не будет опущен оптимизацией

static void nop_func()  {   }
typedef void (*nop_func_t)();
static nop_func_t nop = &nop_func;

for (...)
{
    nop();
}

Ответ 8

    inline void noop( ) {}

самодокументирован