Nie sądzę, aby żadna z odpowiedzi naprawdę wyjaśniała dokładnie, jakie ma skutki uboczne, a nawet co to jest.
constexpr
i const
w przestrzeni nazw / zakresie plików są identyczne, gdy są inicjowane literałem lub wyrażeniem; ale z funkcją const
może być zainicjalizowany przez dowolną funkcję, ale constexpr
zainicjowany przez non-constexpr (funkcja, która nie jest oznaczona constexpr lub wyrażeniem innym niż constexpr) wygeneruje błąd kompilatora. Zarówno constexpr
i const
są niejawnie wewnętrzne powiązanie zmiennych (no faktycznie, nie przetrwać dostać się do etapu łączącej jeśli kompilacji -O1 i silniejszy, a static
nie zmusić kompilator, aby emitować wewnętrznego (lokalnego) na symbol łącznika const
lub constexpr
gdy na -O1 lub silniejszy; dzieje się tak tylko wtedy, gdy weźmiesz adres zmiennej const
iconstexpr
będzie to symbol wewnętrzny, chyba że zostanie wyrażony za pomocąextern
np.extern constexpr/const int i = 3;
należy użyć). W przypadku funkcji constexpr
powoduje , że funkcja na stałe nigdy nie osiągnie etapu łączenia (niezależnie od extern
lub inline
w definicji albo -O0 lub -Ofast), podczas gdy const
nigdy tego nie robi static
i inline
ma taki wpływ tylko na -O1 i wyżej. Kiedy zmienna const
/ constexpr
jest inicjalizowana przez constexpr
funkcję, obciążenie jest zawsze optymalizowane za pomocą dowolnej flagi optymalizacji, ale nigdy nie jest optymalizowane, jeśli funkcja jest tylko static
lub inline
, lub jeśli zmienna nie jest const
/ constexpr
.
Kompilacja standardowa (-O0)
#include<iostream>
constexpr int multiply (int x, int y)
{
return x * y;
}
extern const int val = multiply(10,10);
int main () {
std::cout << val;
}
kompiluje się do
val:
.long 100 //extra external definition supplied due to extern
main:
push rbp
mov rbp, rsp
mov esi, 100 //substituted in as an immediate
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
pop rbp
ret
__static_initialization_and_destruction_0(int, int):
.
.
.
jednak
#include<iostream>
const int multiply (int x, int y)
{
return x * y;
}
const int val = multiply(10,10); //constexpr is an error
int main () {
std::cout << val;
}
Kompiluje do
multiply(int, int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov DWORD PTR [rbp-8], esi
mov eax, DWORD PTR [rbp-4]
imul eax, DWORD PTR [rbp-8]
pop rbp
ret
main:
push rbp
mov rbp, rsp
mov eax, DWORD PTR val[rip]
mov esi, eax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
pop rbp
ret
__static_initialization_and_destruction_0(int, int):
.
.
.
mov esi, 10
mov edi, 10
call multiply(int, int)
mov DWORD PTR val[rip], eax
To wyraźnie pokazuje, że constexpr
powoduje to inicjalizację const/constexpr
zmiennej o zasięgu pliku w czasie kompilacji i nie generuje żadnego globalnego symbolu, natomiast jej niestosowanie powoduje, że inicjalizacja ma miejsce wcześniejmain
w czasie wykonywania.
Kompilowanie przy użyciu -Ofast
Nawet -Ofast nie optymalizuje obciążenia! https://godbolt.org/z/r-mhif , więc potrzebujesz constexpr
constexpr
funkcje mogą być również wywoływane z innych constexpr
funkcji dla tego samego wyniku. constexpr
na funkcji zapobiega także użyciu czegokolwiek, czego nie można zrobić w czasie kompilacji w funkcji; na przykład połączenie z <<
operatorem włączonestd::cout
.
constexpr
zakres bloku zachowuje się tak samo, ponieważ powoduje błąd, jeśli jest inicjowany przez funkcję inną niż constexpr; wartość jest również podstawiana natychmiast.
Ostatecznie jego głównym celem jest jak wbudowana funkcja C, ale jest skuteczna tylko wtedy, gdy funkcja jest używana do inicjalizacji zmiennych o zasięgu pliku (które to funkcje nie mogą wykonać w C, ale mogą to zrobić w C ++, ponieważ umożliwia dynamiczną inicjalizację pliku zmienne zasięgu), z tą różnicą, że funkcja nie może również eksportować globalnego / lokalnego symbolu do linkera, nawet używając extern/static
, co można by zrobić inline
na C; funkcje przypisywania zmiennych o zasięgu blokowym można wprowadzić po prostu za pomocą optymalizacji -O1 bez constexpr
C i C ++.
constexpr
tworzy stałą czasową kompilacji;const
oznacza po prostu, że wartości nie można zmienić.