Що означає «отруїти функцію» в C ++?


96

В самому кінці виступу Скотта Шура "Введення constexpr" на CppCon він запитує "Чи є спосіб отруїти функцію"? Потім він пояснює, що це можна зробити (хоча і нестандартно) шляхом:

  1. Поміщення a 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, до тих пір , як вона ніколи не буде досягнуто під час компіляції викликів функції (тому що ви не можете кинути виняток під час компіляції, це по суті своїй динамічної операції, як виділення пам'яті). Отже, викидний вираз, який посилається на невизначений символ, не буде використовуватися під час викликів під час компіляції (оскільки це не зможе скомпілювати) і не може бути використаний під час виконання, оскільки невизначений символ спричиняє помилку лінкера.

Оскільки невизначений символ не використовується "odr-used" під час викликів функції компіляції, на практиці компілятор не створюватиме посилання на символ, тому це нормально, що він невизначений.

Це корисно? Він демонструє, як це робити, не обов’язково кажучи, що це гарна ідея чи корисна справа. Якщо вам потрібно це зробити з якихось причин, тоді його техніка може вирішити вашу проблему. Якщо у вас немає потреби в цьому, вам не потрібно турбуватися про це.

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

NB: Продуктивність функції constexpr, яка використовується під час компіляції, насправді не є важливою, оскільки вона все одно не має накладних витрат на час виконання. Це може сповільнити вашу компіляцію, змусивши компілятор виконати додаткову роботу, але це не призведе до витрат під час виконання.


1
Я читав текст слайда, але не бачив зв'язку з терміном, який він використовував. Зараз очевидно, що ви це пояснили, але я тоді цього не бачив. Щиро дякую за цю чудову відповідь - я просто люблю цей веб-сайт.
sudo make install

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

17

"Отруєння" ідентифікатора означає, що будь-яке посилання на ідентифікатор після "отруєння" є серйозною помилкою компілятора. Цей прийом може бути використаний, наприклад, для жорсткої застарілості (функція ЗАСТОРОЖЕНА, ніколи не використовуйте її!).

В GCC традиційно був Прагма для цього: #pragma GCC poison.


1
Так, але не зовсім у тому значенні, яке використовується в цій розмові.
TC

@TC, гаразд, мабуть, я мав би це переглянути перед тим, як відповісти :)
Сергій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.