Czy kiedykolwiek jest źle oznaczać constexpr funkcji C ++?

26

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 constexprfunkcję, pozwalając mi jej używać podczas definiowania constexprzmiennych, 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 constexprkontekś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ć?

Xirema
źródło
1
Jedyne, co mogłem wymyślić, to błędy kompilatora. Możliwe, że rekurencyjne wywołanie funkcji constexpr może spowodować naprawdę powolny krok kompilacji lub nawet awarię kompilatora z powodu awarii pamięci.
Zan Lynx

Odpowiedzi:

19

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 constexprkwalifikator 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ć constexprfunkcji, 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.

amon
źródło
12

Oznaczenie funkcji jako constexprrównież czyni ją funkcją wbudowaną § [dcl.constexpr] / 1:

Funkcja lub element danych statycznych zadeklarowany za pomocą specyfikatora constexpr jest domyślnie funkcją wbudowaną lub zmienną (7.1.6).

inlinez 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, że constexprfunkcje muszą być:

  1. ograniczone do użycia w jednej jednostce tłumaczeniowej, lub
  2. zdefiniowane w nagłówku.

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) constexprpo 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.

constexprFunkcja jest również ograniczone w pewnym sensie, tak dla niektórych funkcji nie może być opcja w ogóle. Ograniczenia obejmują:

  1. funkcje wirtualne nie mogą być constexpr.
  2. jego typ zwracany musi być „dosłowny” (np. nie ma obiektów z nietrywialnymi lekarzami lub lekarzami).
  3. wszystkie jego parametry muszą być literalne.
  4. ciało funkcji nie może zawierać trybloku.
  5. nie może zawierać definicji zmiennej typu nieliteralnego ani niczego o czasie przechowywania statycznym lub wątku.

Pominąłem kilka raczej niejasnych rzeczy (np. Nie może również zawierać instrukcji gotolub asmoś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ł.

Jerry Coffin
źródło
„nie może być wirtualny (do C ++ 20)” Zastanawiam się, jak funkcja wirtualna może być constexpr? Co robią kompilatory?
chaosink