Wydaje mi się, że posiadanie „funkcji, która zawsze zwraca 5”, łamie lub osłabia znaczenie „wywoływania funkcji”. Musi być jakiś powód lub potrzeba takiej możliwości, inaczej nie byłoby w C ++ 11. Dlaczego tam jest
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
Wydaje mi się, że gdybym napisał funkcję, która zwraca wartość dosłowną, i podszedłbym do recenzji kodu, ktoś powiedziałby mi, że powinienem zadeklarować stałą wartość zamiast pisać return 5.
constexpr
? Jeśli tak, widzę użycie.const
. W rzeczywistości mandat jest użyteczny ! Wymiary tablicy są kanonicznym przykładem.Odpowiedzi:
Załóżmy, że robi to coś bardziej skomplikowanego.
Teraz masz coś, co można ocenić do stałej, zachowując dobrą czytelność i pozwalając na nieco bardziej złożone przetwarzanie niż tylko ustawienie stałej na liczbę.
Zasadniczo zapewnia dobrą pomoc w utrzymaniu, ponieważ staje się bardziej oczywiste, co robisz. Weźmy
max( a, b )
na przykład:Jest to dość prosty wybór, ale oznacza to, że jeśli wywołujesz
max
ze stałymi wartościami, jest to jawnie obliczane w czasie kompilacji, a nie w czasie wykonywania.Kolejnym dobrym przykładem może być
DegreesToRadians
funkcja. Wszyscy uważają stopnie za łatwiejsze do odczytania niż radiany. Chociaż możesz wiedzieć, że 180 stopni jest w radianach, jest o wiele wyraźniej napisane w następujący sposób:Wiele dobrych informacji tutaj:
http://en.cppreference.com/w/cpp/language/constexpr
źródło
Wprowadzenie
constexpr
nie został wprowadzony jako sposób poinformowania implementacji, że coś można ocenić w kontekście, który wymaga stałej ekspresji ; implementacje zgodne były w stanie to udowodnić przed C ++ 11.Coś, czego implementacja nie może udowodnić, jest intencją określonego fragmentu kodu:
Czym byłby świat bez
constexpr
?Załóżmy, że tworzysz bibliotekę i zdajesz sobie sprawę, że chcesz być w stanie obliczyć sumę każdej liczby całkowitej w przedziale
(0,N]
.Brak zamiaru
Kompilator może łatwo udowodnić, że powyższą funkcję można wywołać w wyrażeniu stałym jeśli przekazany argument jest znany podczas tłumaczenia; ale nie zadeklarowaliście tego jako zamiaru - tak się po prostu stało.
Teraz pojawia się ktoś inny, czyta twoją funkcję, wykonuje taką samą analizę jak kompilator; „ Och, ta funkcja nadaje się do wyrażenia ciągłego!” i zapisuje następujący fragment kodu.
Optymalizacja
Ty, jako „niesamowity” programista bibliotek, decydujesz, że
f
powinieneś buforować wynik podczas wywoływania; kto chciałby ciągle obliczać ten sam zestaw wartości?Wynik
Wprowadzając swoją głupią optymalizację, po prostu przerwałeś każde użycie funkcji, które zdarzyło się w kontekście ciągłego wyrażania wymagane było .
Nigdy nie obiecałeś, że funkcja będzie użyteczna w ciągłym wyrażaniu i bez
constexpr
niej nie byłoby sposobu na zapewnienie takiej obietnicy.Dlaczego więc potrzebujemy
constexpr
?Podstawowym zastosowaniem constexpr jest deklarowanie zamiaru .
Jeśli jednostka nie jest oznaczona jako
constexpr
- nigdy nie była przeznaczona do użycia w wyrażeniu stałym ; a nawet jeśli tak jest, polegamy na kompilatorze w diagnozowaniu takiego kontekstu (ponieważ nie uwzględnia on naszych zamiarów).źródło
constexpr
wyrażeniach. Innymi słowy, prawie wszystko może być opatrzone adnotacjamiconstexpr
(może kiedyś po prostu odejdzie z tego powodu?) I chyba że ktoś ma kryterium, kiedy użyć,constexpr
czy nie, prawie cały kod zostanie napisany jako taki .I/O
,syscall
adynamic memory allocation
zdecydowanie cann't być oznaczone jakoconstexpr
Zresztą nie wszystko powinno byćconstexpr
.constexpr
jest gwarancją pewnego rodzaju zachowania. Tak jakconst
robi.int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)];
nie mogę jej nigdzie skompilować?Weź
std::numeric_limits<T>::max()
: z jakiegokolwiek powodu, jest to metoda.constexpr
przydałoby się tutaj.Kolejny przykład: chcesz zadeklarować tablicę C (lub a
std::array
), która jest tak duża jak inna tablica. Obecnie można to zrobić w następujący sposób:Ale czy nie byłoby lepiej móc pisać:
Dzięki temu
constexpr
możesz:źródło
constexpr
wymusza na kompilatorze, aby funkcja zwracała wartość czasu kompilacji (jeśli to możliwe).constexpr
nie można go użyć w deklaracji rozmiaru tablicy ani jako argumentu szablonu, niezależnie od tego, czy wynikiem wywołania funkcji jest stała czasu kompilacji, czy nie. Te dwa są w zasadzie jedynymi przypadkami użycia,constexpr
ale przynajmniej szablonowy przypadek użycia jest dość ważny.-pedantic
opcji, a zostanie ona oznaczona jako błąd.constexpr
funkcje są naprawdę ładne i stanowią doskonały dodatek do c ++. Masz jednak rację, że większość problemów, które rozwiązuje, można nieelegancko obejść za pomocą makr.Jednak jedno z zastosowań
constexpr
nie ma odpowiedników w typie C ++ 03.źródło
four
nie jest rozpoznawany. Musiałem naprawdę wykopać, kto bierze adres mojejstatic const
zmiennej.four
niefive
są objęte zakresem.enum class
typ, naprawia niektóre problemy wyliczania.Z tego, co przeczytałem, potrzeba constexpr wynika z problemu w metaprogramowaniu. Klasy cech mogą mieć stałe reprezentowane jako funkcje, pomyśl: numeric_limits :: max (). Dzięki constexpr tego typu funkcje mogą być używane w metaprogramowaniu lub jako ograniczenia tablic itp.
Innym przykładem z góry mojej głowy jest to, że dla interfejsów klasowych możesz chcieć, aby typy pochodne definiowały własne stałe dla niektórych operacji.
Edytować:
Po wywiercenie na tak, to wygląda na to inni mają wymyślić kilka przykładów tego, co może być możliwe z constexprs.
źródło
constexpr
jest szczególnie przydatny w kompilatorze z potężnym systemem oceny wyrażeń w czasie kompilacji. C ++ naprawdę nie ma peerów w tej domenie. (to mocna pochwała dla C ++ 11, IMHO)Z przemówienia Stroustrupa na „Going Native 2012”:
źródło
Innym zastosowaniem (jeszcze nie wspomnianym) są
constexpr
konstruktory. Umożliwia to tworzenie stałych czasowych kompilacji, które nie muszą być inicjowane podczas działania.Po sparowaniu z literałami zdefiniowanymi przez użytkownika uzyskasz pełne wsparcie dla literałów zdefiniowanych przez użytkownika.
źródło
Kiedyś był metaprogramowanie:
Wierzę, że
constexpr
został wprowadzony, aby umożliwić pisanie takich konstrukcji bez potrzeby korzystania z szablonów i dziwnych konstrukcji ze specjalizacją, SFINAE i innymi rzeczami - ale dokładnie tak, jakbyś pisał funkcję czasu wykonywania, ale z gwarancją, że wynik zostanie określony podczas kompilacji -czas.Należy jednak pamiętać, że:
Skompiluj to,
g++ -O3
a zobaczysz, żefact(10)
jest to ewaluowane w czasie kompilacji!Kompilator obsługujący VLA (kompilator C w trybie C99 lub kompilator C ++ z rozszerzeniami C99) może nawet umożliwiać:
Ale w tej chwili jest to niestandardowy C ++ -
constexpr
wygląda na to, że można to zwalczyć (nawet bez VLA, w powyższym przypadku). Nadal istnieje problem z koniecznością posiadania „formalnych” wyrażeń stałych jako argumentów szablonu.źródło
std::array<int, fact(2)>
a zobaczysz, że fakt () nie jest oceniany podczas kompilacji. Po prostu optymalizator GCC wykonuje dobrą robotę.Właśnie zacząłem zmieniać projekt na c ++ 11 i natrafiłem na całkowicie dobrą sytuację dla constexpr, który oczyszcza alternatywne metody wykonywania tej samej operacji. Kluczową kwestią jest to, że możesz umieścić funkcję w deklaracji rozmiaru tablicy tylko wtedy, gdy jest zadeklarowana jako constexpr. Jest wiele sytuacji, w których widzę, że jest to bardzo przydatne, gdy idzie o krok naprzód z obszarem kodu, w który jestem zaangażowany.
źródło
static inline constexpr const auto
prawdopodobnie jest lepiej.Wszystkie pozostałe odpowiedzi są świetne, chcę tylko dać fajny przykład jednej rzeczy, którą możesz zrobić z constexpr, która jest niesamowita. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) to analizator składni HTML i silnik szablonów. Oznacza to, że możesz umieścić HTML i wydostać się z drzewa, którym można manipulować. Wykonanie analizy składniowej w czasie kompilacji może zapewnić dodatkową wydajność.
Z przykładu strony github:
źródło
Twój podstawowy przykład podaje ten sam argument, co argument samych stałych. Dlaczego warto korzystać
nad
Ponieważ jest to o wiele łatwiejsze w utrzymaniu. Korzystanie z constexpr jest dużo, dużo szybsze w pisaniu i czytaniu niż istniejące techniki metaprogramowania.
źródło
Może włączyć nowe optymalizacje.
const
tradycyjnie jest wskazówką dla systemu typów i nie można jej użyć do optymalizacji (np.const
funkcja członka możeconst_cast
i modyfikuje obiekt mimo to, legalnie, więcconst
nie można ufać optymalizacji).constexpr
oznacza, że wyrażenie jest naprawdę stałe, pod warunkiem, że dane wejściowe do funkcji są stałe. Rozważać:Jeśli zostanie to ujawnione w innym module, kompilator nie może ufać, że
GetNumber()
nie zwróci różnych wartości za każdym razem, gdy jest wywoływany - nawet kolejno bez żadnych wywołań pośrednich między nimi - ponieważconst
mógł zostać odrzucony w implementacji. (Oczywiście każdy programista, który to zrobił, powinien zostać zastrzelony, ale język na to pozwala, dlatego kompilator musi przestrzegać zasad.)Dodawanie
constexpr
:Kompilator może teraz zastosować optymalizację, w której zwracana wartość
GetNumber()
jest buforowana, i eliminować dodatkowe wywołaniaGetNumber()
, ponieważconstexpr
jest to silniejsza gwarancja, że wartość zwracana nie ulegnie zmianie.źródło
const
można go wykorzystać do optymalizacji ... Nieokreślone zachowanie polega na modyfikacji stałej zdefiniowanej wartości nawet poconst_cast
IIRC. Spodziewałbym się, że będzie spójny dlaconst
funkcji składowych, ale muszę to sprawdzić za pomocą standardu. Oznaczałoby to, że kompilator może bezpiecznie przeprowadzać tam optymalizacje.int x
vs.const int x
), to można go bezpiecznie zmodyfikować,const_cast
odsuwając const na wskaźniku / odnośniku do niego. W przeciwnym razieconst_cast
zawsze wywoływałby niezdefiniowane zachowanie i byłby bezużyteczny :) W tym przypadku kompilator nie ma informacji o trwałości oryginalnego obiektu, więc nie może powiedzieć.int GetNumber() const = 0;
) powinien zadeklarowaćGetNumber()
metodę wirtualną. Druga (constexpr int GetNumber() const = 0;
) jest nieprawidłowa, ponieważ czysty specyfikator (= 0
) sugeruje, że metoda jest wirtualna, ale constexpr nie może być wirtualny (ref: en.cppreference.com/w/cpp/language/constexpr )Kiedy stosować
constexpr
:źródło
constexpr
należy preferować makra lub preprocesoryconst
.Przydaje się do czegoś takiego
Połącz to z klasą cech lub podobną, a stanie się to całkiem przydatne.
źródło