Historia pytania
C++ początkowo wspierał tylko makra (#define) i stałe (const). Jednak do celów określenia wartości w czasie kompilacji było to niewystarczające. W C++11 wprowadzono słowo kluczowe constexpr, które pozwala na obliczanie wartości już na etapie kompilacji, a nie tylko w czasie wykonywania programu.
Problem
Przed pojawieniem się constexpr wiele zadań należało rozwiązywać za pomocą makr (prymitywnego zastępnika tekstowego bez bezpieczeństwa typów) lub za pomocą const, który nie zawsze gwarantował wykonanie wyrażenia na etapie kompilacji. To utrudniało optymalizację programu i prowadziło do mniej przewidywalnego zachowania.
Rozwiązanie
constexpr gwarantuje kompilatorowi, że zadeklarowane wyrażenie będzie obliczone w czasie kompilacji, jeśli to możliwe. Używa się go do deklaracji funkcji, zmiennych, a nawet konstruktorów i metod klas, które bezpiecznie i efektywnie są obliczane podczas kompilacji.
Przykład kodu:
constexpr int Square(int x) { return x * x; } constexpr int size = Square(5); // size obliczane na etapie kompilacji const int arr[size] = {}; // można użyć jako rozmiaru tablicy
Kluczowe cechy:
Czy można używać dowolnej funkcji jako constexpr?
Nie. Funkcja musi spełniać szereg ograniczeń: być wystarczająco prosta, zawierać jedną linię return (do C++14) lub tylko kod do obliczeń stałych (od C++14 wzwyż).
constexpr int f(int x) { return x + 2; } // ok constexpr int g(int x) { int y = x + 2; return y; } // do C++14: nie kompiluje się! po — można
Czy wszystkie zmienne constexpr mogą być obliczane w czasie kompilacji?
Nie. Jeśli podczas inicjalizacji używane jest nie-stałe wartości lub wyrażenie nie udaje się obliczyć w czasie kompilacji, wystąpi błąd.
int val; // constexpr int x = f(val); // Błąd: val nie został zainicjalizowany!
Jaka jest różnica między constexpr a const?
const gwarantuje tylko niemożność zmiany, ale nie gwarantuje obliczenia w czasie kompilacji. constexpr wymaga obliczenia wartości w czasie kompilacji (jeśli to możliwe).
const int x = time(nullptr); // ok, ale obliczane w czasie wykonywania constexpr int y = 42; // ok, obliczane w czasie kompilacji
Programista używa #define PI 3.14 do wszystkich obliczeń powierzchni koła.
Zalety:
Wady:
Programista używa constexpr double PI = 3.141592653589793; oraz szablonowych funkcji constexpr do obliczeń.
Zalety:
Wady: