Na samym końcu wystąpienia Scotta Schurra „Wprowadzenie constexpr
” na CppCon pyta „Czy istnieje sposób na zatrucie funkcji”? Następnie wyjaśnia, że można to zrobić (choć w niestandardowy sposób) poprzez:
- Umieszczenie
throw
wconstexpr
funkcji - Deklarowanie nierozwiązanego problemu
extern const char*
- Odwoływanie się do nierozwiązanych
extern
wthrow
Czuję, że trochę mi tu brakuje głębi, ale jestem ciekawy:
- Co to znaczy „zatruwać funkcję”?
- Jakie jest znaczenie / przydatność opisanej przez niego techniki?
constexpr
funkcji jest oceniane w czasie kompilacji.constexpr
funkcja może być używana w czasie kompilacji lub w czasie wykonywania. Czy to jest sposób na wymuszenie tego, aby nie można było go używać w czasie wykonywania? Kiedy jest to przydatne?constexpr
funkcja często nie jest najbardziej wydajną implementacją z powodu ograniczeń, więc można nie chcieć, aby była oceniana w czasie wykonywania; a może jest to przypadek błędu (jak w jego przykładzie).Odpowiedzi:
Ogólnie odnosi się to do uczynienia funkcji bezużyteczną, np. Jeśli chcesz zakazać używania alokacji dynamicznej w programie, możesz ją „zatruć”,
malloc
aby nie można jej było używać.W filmie używa go w bardziej konkretny sposób, co jest jasne, jeśli przeczytasz slajd, który jest wyświetlany, gdy mówi o zatruciu funkcji, który mówi „Sposób na wymuszenie tylko czasu kompilacji?”
Mówi więc o „zatruciu” funkcji, aby uniemożliwić jej wywołanie w czasie wykonywania, więc można ją wywołać tylko w stałych wyrażeniach. Technika polega na tym, aby mieć gałąź w funkcji, która nigdy nie jest pobierana, gdy jest wywoływana w kontekście czasu kompilacji, i sprawić, by ta gałąź zawierała coś, co spowoduje błąd.
throw
Ekspresja jest dozwolone w funkcji constexpr, dopóki nie zostanie osiągnięta podczas inwokacji wbudowanego wskaźnika funkcji (ponieważ nie można rzucać wyjątek w czasie kompilacji, to z natury dynamiczne działanie, jak przydzielania pamięci). Tak więc wyrażenie rzutowania, które odwołuje się do niezdefiniowanego symbolu, nie będzie używane podczas wywołań w czasie kompilacji (ponieważ kompilacja nie powiodłaby się) i nie może być używana w czasie wykonywania, ponieważ niezdefiniowany symbol powoduje błąd konsolidatora.Ponieważ niezdefiniowany symbol nie jest „używany przez ODR” w wywołaniach funkcji w czasie kompilacji, w praktyce kompilator nie utworzy odniesienia do symbolu, więc jest OK, że jest on niezdefiniowany.
Czy to jest przydatne? Pokazuje, jak to zrobić, niekoniecznie mówiąc, że to dobry pomysł lub bardzo przydatny. Jeśli z jakiegoś powodu musisz to zrobić, jego technika może rozwiązać twój problem. Jeśli nie potrzebujesz tego, nie musisz się tym martwić.
Jednym z powodów, dla których może to być przydatne, jest sytuacja, gdy wersja operacji w czasie kompilacji nie jest tak wydajna, jak mogłaby być. Istnieją ograniczenia dotyczące rodzaju wyrażeń dozwolonych w funkcji constexpr (szczególnie w C ++ 11, niektóre ograniczenia zostały usunięte w C ++ 14). Więc możesz mieć dwie wersje funkcji do wykonywania obliczeń, jedną, która jest optymalna, ale używa wyrażeń, które nie są dozwolone w funkcji constexpr, i jedną, która jest prawidłową funkcją constexpr, ale działałaby słabo, gdyby została wywołana w run- czas. Możesz zatruć nieoptymalną wersję, aby upewnić się, że nigdy nie jest używana do wywołań w czasie wykonywania, zapewniając, że bardziej wydajna (nie będąca constexpr) wersja jest używana do wywołań w czasie wykonywania.
Uwaga: Wydajność funkcji constexpr używanej w czasie kompilacji nie jest naprawdę ważna, ponieważ i tak nie ma narzutu czasu wykonywania. Może spowolnić twoją kompilację, zmuszając kompilator do dodatkowej pracy, ale nie będzie to miało żadnego kosztu wydajności w czasie wykonywania.
źródło
„Zatrucie” identyfikator oznacza, że każde odniesienie do identyfikatora po „zatruciu” jest twardym błędem kompilatora. Technika ta może być użyta na przykład do trwałego wycofania (funkcja JEST przestarzała, nigdy jej nie używaj!).
W GCC tradycyjnie było Pragma do tego:
#pragma GCC poison
.źródło