Biorąc pod uwagę bardzo trywialną funkcję,
int transform(int val) {
return (val + 7) / 8;
}
Powinno być bardzo oczywiste, że łatwo jest przekształcić tę funkcję w constexpr
funkcję, pozwalając mi jej używać podczas definiowania constexpr
zmiennych, tak jak:
constexpr int transform(int val) {
return (val + 7) / 8;
}
Zakładam, że jest to wyłącznie ulepszenie, ponieważ funkcja może być nadal wywoływana w constexpr
kontekście innym niż kontekst, a teraz można jej również użyć do zdefiniowania zmiennych stałych czasu kompilacji.
Moje pytanie brzmi: czy są sytuacje, w których jest to zły pomysł? Czy, tworząc tę funkcję constexpr
, czy mogę kiedykolwiek spotkać się z sytuacją, w której ta funkcja nie będzie już użyteczna w określonych okolicznościach lub gdzie będzie źle funkcjonować?
Odpowiedzi:
Ma to znaczenie tylko wtedy, gdy funkcja jest częścią interfejsu publicznego i chcesz zachować przyszłe wersje binarnego interfejsu API. W takim przypadku musisz dokładnie przemyśleć, jak chcesz rozwinąć swój interfejs API i gdzie potrzebujesz punktów rozszerzenia do przyszłych zmian.
To sprawia, że
constexpr
kwalifikator jest nieodwołalną decyzją projektową. Nie możesz usunąć tego kwalifikatora bez niezgodnej zmiany w interfejsie API. Ogranicza to także sposób implementacji tej funkcji, np. Nie będzie można wykonywać żadnego logowania w ramach tej funkcji. Nie każda trywialna funkcja pozostanie trywialna w wieczności.Oznacza to, że powinieneś najlepiej używać
constexpr
funkcji, które są z natury czystymi funkcjami i które byłyby faktycznie przydatne w czasie kompilacji (np. Do metaprogramowania szablonów). Nie byłoby dobrze tworzyć funkcje constexpr tylko dlatego, że obecna implementacja bywa zdolna do constexpr.Tam, gdzie ocena czasu kompilacji nie jest konieczna, bardziej odpowiednie byłoby użycie funkcji wbudowanych lub funkcji z wewnętrznym łączeniem
constexpr
. Wszystkie te warianty mają wspólną cechę, że treść funkcji jest „publiczna” i jest dostępna w tej samej jednostce kompilacji co lokalizacja wywołania.Jeśli dana funkcja nie jest częścią stabilnego, publicznego interfejsu API, nie stanowi to większego problemu, ponieważ można dowolnie zmieniać projekt. Ale ponieważ teraz kontrolujesz wszystkie witryny wywołań, nie trzeba zaznaczać funkcji constexpr „na wszelki wypadek”. Ci wiedzieć , czy używasz tej funkcji w kontekście constexpr. Dodanie niepotrzebnie restrykcyjnych kwalifikatorów można wówczas uznać za zaciemnianie.
źródło
Oznaczenie funkcji jako
constexpr
również czyni ją funkcją wbudowaną § [dcl.constexpr] / 1:inline
z kolei oznacza, że musisz zawrzeć definicję tej funkcji w każdej jednostce tłumaczeniowej, w której może być używana. Zasadniczo oznacza to, żeconstexpr
funkcje muszą być:Najbardziej typowe funkcje, które chcesz zadeklarować w nagłówku i zdefiniować w pliku źródłowym (i wszystko, co ich używa, zawiera tylko nagłówek, a następnie łącza do pliku obiektowego tego źródła)
constexpr
po prostu nie działa.Teoretycznie przypuszczam, że możesz po prostu przenieść wszystko do nagłówków i mieć tylko jeden plik źródłowy, który zawiera tylko wszystkie nagłówki, ale drastycznie zaszkodzi to czasom kompilacji, a dla większości poważnych projektów kompilacja wymaga ogromnej ilości pamięci.
constexpr
Funkcja jest również ograniczone w pewnym sensie, tak dla niektórych funkcji nie może być opcja w ogóle. Ograniczenia obejmują:constexpr
.try
bloku.Pominąłem kilka raczej niejasnych rzeczy (np. Nie może również zawierać instrukcji
goto
lubasm
oświadczenia), ale masz pomysł - w przypadku kilku rzeczy to po prostu nie zadziała.Podsumowując: tak, jest całkiem sporo sytuacji, w których byłby to zły pomysł.
źródło