Что значит «отравить функцию» в C ++?


96

В самом конце выступления Скотта Шурра «Представляем constexpr» на CppCon он спрашивает: «Есть ли способ отравить функцию»? Затем он объясняет, что это можно сделать (хотя и нестандартным способом):

  1. Ввод throwв constexprфункцию
  2. Объявление неразрешенной extern const char*
  3. Ссылаясь на нерешенные externвthrow

Я чувствую, что здесь я немного не в себе, но мне любопытно:

  • Что значит «отравить функцию»?
  • В чем важность / полезность описываемой им техники?

1
Никогда не слышал об этом термине, поясните, пожалуйста, кратким примером!
πάντα ῥεῖ

6
@ πάνταῥεῖ, я только что уточнил. Это термин «широко известен в
узких

4
Он говорит об обеспечении того, чтобы каждый вызов constexprфункции оценивался во время компиляции.
TC

@TC Right - он упомянул, что constexprфункцию можно использовать либо во время компиляции, либо во время выполнения. Так это способ заставить его использовать во время выполнения? Когда это полезно?
sudo make install

3
В особенности в C ++ 11 constexprфункция часто не является наиболее эффективной реализацией из-за ограничений, поэтому может не потребоваться ее оценка во время выполнения; или, может быть, это ошибка (как в его примере).
TC

Ответы:


106

В общем, это относится к тому, чтобы сделать функцию непригодной для использования, например, если вы хотите запретить использование динамического выделения в программе, вы можете «отравить» mallocфункцию, чтобы ее нельзя было использовать.

В видео он использует это более определенным образом, что становится ясно, если вы прочитаете слайд, который отображается, когда он говорит об отравлении функции, где говорится: «Способ принудительно использовать только время компиляции?»

Итак, он говорит об «отравлении» функции, чтобы сделать ее не вызываемой во время выполнения, поэтому ее можно вызывать только в постоянных выражениях. Метод состоит в том, чтобы иметь ветвь в функции, которая никогда не выполняется при вызове в контексте времени компиляции, и чтобы эта ветвь содержала что-то, что вызовет ошибку.

throwВыражение допускается в функции constexpr, до тех пор , как она никогда не будет достигнуто во время компиляции вызовов функции (потому что вы не можете бросить исключение во время компиляции, это по сути своей динамической операции, как выделение памяти). Таким образом, выражение throw, которое ссылается на неопределенный символ, не будет использоваться во время вызовов во время компиляции (потому что это не может быть скомпилировано) и не может использоваться во время выполнения, потому что неопределенный символ вызывает ошибку компоновщика.

Поскольку неопределенный символ не используется "odr" при вызовах функции во время компиляции, на практике компилятор не будет создавать ссылку на символ, поэтому нормально, что он не определен.

Это полезно? Он демонстрирует, как это сделать, не обязательно говоря, что это хорошая идея или широко полезная идея. Если по какой-то причине вам нужно это сделать, его методика может решить вашу проблему. Если вам это не нужно, вам не о чем беспокоиться.

Одна из причин, по которой это может быть полезно, - это когда версия некоторой операции во время компиляции не так эффективна, как могла бы быть. Существуют ограничения на типы выражений, разрешенные в функции constexpr (особенно в C ++ 11, некоторые ограничения были сняты в C ++ 14). Таким образом, у вас может быть две версии функции для выполнения вычислений: одна оптимальная, но использует выражения, которые недопустимы в функции constexpr, и одна, которая является допустимой функцией constexpr, но будет плохо работать, если будет вызвана во время выполнения. время. Вы можете отравить субоптимальный вариант, чтобы гарантировать, что он никогда не будет использоваться для вызовов времени выполнения, гарантируя, что более эффективная версия (не constexpr) будет использоваться для вызовов времени выполнения.

NB. Производительность функции constexpr, используемой во время компиляции, на самом деле не важна, потому что в любом случае у нее нет накладных расходов во время выполнения. Это может замедлить вашу компиляцию, заставив компилятор выполнять дополнительную работу, но не будет иметь никаких затрат на производительность во время выполнения.


1
Я прочитал текст слайда, но не увидел связи с термином, который он использовал. Теперь очевидно, что вы это объяснили, но в то время я этого не видел. Большое спасибо за этот отличный ответ - мне просто нравится этот сайт.
sudo make install

@PravasiMeet, задайте свой вопрос, не перехватывайте комментарии чужого вопроса о чем-то другом. Простым решением было бы определить его как удаленный в каждой единице перевода или заменить его собственным определением, которое ссылается на неопределенный символ.
Джонатан Уэйкли,

17

«Отравление» идентификатора означает, что любая ссылка на идентификатор после «отравления» является серьезной ошибкой компилятора. Этот метод может использоваться, например, для отмены поддержки (функция устарела, никогда не используйте ее!).

В GCC традиционно был Прагма для этого: #pragma GCC poison.


1
Да, но не совсем в том смысле, который используется в этом разговоре.
TC

@TC, ладно, наверное, стоит посмотреть, прежде чем отвечать :)
SergeyA
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.