Nie sądzę, aby żadna z odpowiedzi naprawdę wyjaśniała dokładnie, jakie ma skutki uboczne, a nawet co to jest.
constexpri constw przestrzeni nazw / zakresie plików są identyczne, gdy są inicjowane literałem lub wyrażeniem; ale z funkcją constmoże być zainicjalizowany przez dowolną funkcję, ale constexprzainicjowany przez non-constexpr (funkcja, która nie jest oznaczona constexpr lub wyrażeniem innym niż constexpr) wygeneruje błąd kompilatora. Zarówno constexpri constsą niejawnie wewnętrzne powiązanie zmiennych (no faktycznie, nie przetrwać dostać się do etapu łączącej jeśli kompilacji -O1 i silniejszy, a staticnie zmusić kompilator, aby emitować wewnętrznego (lokalnego) na symbol łącznika constlub constexprgdy na -O1 lub silniejszy; dzieje się tak tylko wtedy, gdy weźmiesz adres zmiennej consticonstexpr 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 constexprpowoduje , że funkcja na stałe nigdy nie osiągnie etapu łączenia (niezależnie od externlub inlinew definicji albo -O0 lub -Ofast), podczas gdy constnigdy tego nie robi statici inlinema taki wpływ tylko na -O1 i wyżej. Kiedy zmienna const/ constexprjest inicjalizowana przez constexprfunkcję, obciążenie jest zawsze optymalizowane za pomocą dowolnej flagi optymalizacji, ale nigdy nie jest optymalizowane, jeśli funkcja jest tylko staticlub 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 constexprpowoduje to inicjalizację const/constexprzmiennej 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
constexprfunkcje mogą być również wywoływane z innych constexprfunkcji dla tego samego wyniku. constexprna 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 .
constexprzakres 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ć inlinena C; funkcje przypisywania zmiennych o zasięgu blokowym można wprowadzić po prostu za pomocą optymalizacji -O1 bez constexprC i C ++.
constexprtworzy stałą czasową kompilacji;constoznacza po prostu, że wartości nie można zmienić.