Co decyduje o tym, czy funkcja constexpr jest wyrażeniem stałym?
(używany kompilator to gcc z c ++ 17, o ile wiem (trudno to znaleźć w Visual Studio))
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
increment( v );
return v;
}
int main( )
{
cout << f( ) << '\n';
}
Powyższy kod daje błąd przy kompilacji:
Funkcja constexpr „f” nie może skutkować wyrażeniem stałym.
Jak rozumiem, increment
dzieje się tak dlatego, że funkcja nie jest constexpr. Co mnie wprawia w zakłopotanie, to fakt, że następujący kod kompiluje się dobrze:
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
for( int i = 0; i < 1; ++i )
{
increment( v );
}
return v;
}
int main( )
{
cout << f( ) << '\n';
}
Ten kod jest funkcjonalnie taki sam i kompiluje się, mimo że inkrementacja nadal nie jest wartością constexpr. Nie rozumiem, jak to możliwe, że pętla for przez zakres [0, 1) powoduje, że kompilator zdaje sobie sprawę, że funkcja f
jest w rzeczywistości constexpr.
Jeśli ktoś może dać wgląd w constexpr w języku c ++ i tę pozorną niespójność, byłbym bardzo wdzięczny.
Odpowiedzi
Oba programy są „źle sformułowane, nie wymaga diagnostyki”, zgodnie z [dcl.constexpr] / 6 :
W przypadku funkcji constexpr lub konstruktora constexpr, który nie jest domyślny ani szablon, jeśli nie istnieją żadne wartości argumentów, takie że wywołanie funkcji lub konstruktora mogłoby być wartościowanym podwyrażeniem podstawowego wyrażenia stałego lub, w przypadku konstruktora, oszacowanym wyrażeniem podrzędnym inicjalizacja pełnego wyrażenia jakiegoś obiektu o stałej inicjalizacji ( [basic.start.static] ), program jest źle sformułowany, nie jest wymagana diagnostyka.
To trochę dziwne, że gcc po prostu nie zauważa problemu z drugim programem, ale nadal jest zgodny.
Zauważ, że diagnostyka byłaby wymagana, gdyby f
została użyta w kontekście, który faktycznie wymaga na przykład stałego wyrażenia constexpr int n = f();
.
Niektóre rzeczy nigdy nie są dozwolone w funkcji constexpr. Wymagają one diagnostyki (zazwyczaj komunikatu o błędzie), nawet jeśli funkcja nigdy nie jest używana w stałym wyrażeniu - zobacz odpowiedź cigien . Jednak programy, o których mowa, nie naruszają żadnych z tych bardziej rygorystycznych zasad.
Ponieważ nie wywołujesz f
wyrażenia stałego, twoje pytanie brzmi, czy kompilator jest wymagany do zdiagnozowania tego, f
czego nie można wywołać w wyrażeniu stałym, opierając się wyłącznie na jego definicji .
Wymagania dotyczące definicji z constexpr
funkcji są wymienione tutaj :
Definicja funkcji constexpr spełnia następujące wymagania:
(3.1) jego zwracany typ (jeśli istnieje) będzie typem dosłownym;
(3.2) każdy z jego typów parametrów jest typem literalnym;
(3.3) nie będzie to coroutine;
(3.4) jeśli funkcja jest konstruktorem lub destruktorem, jej klasa nie powinna mieć żadnych wirtualnych klas bazowych;
(3.5) jego korpus funkcyjny nie powinien obejmować
(3.5.1) instrukcja goto,
(3.5.2) etykieta identyfikacyjna,
(3.5.3) definicja zmiennej typu nieliteralnego lub statycznego lub wątkowego czasu przechowywania.
Jak widać, definicja f
nie narusza żadnego z wymagań na liście. Więc kompilator jest zgodny, jeśli zdecyduje się tego nie diagnozować.
Jak wskazano w odpowiedzi Ascheplera , constexpr
takie funkcje f
nie mogą być wywoływane w ciągłym wyrażeniu, ale nie są diagnozowane jako takie, są uważane za źle sformułowane - niewymagane diagnostycznie.
W rzeczywistości nie „dzwonisz” f
w czasie kompilacji.
jeśli twoja główna funkcja zawiera: static_assert(f() == 1, "f() returned 1");
Podejrzewam, że wystąpiłby błąd „f () nie jest wyrażeniem stałym”.
Oto powiązane pytanie
Standard wymaga, aby constexpr
funkcja była możliwa do oszacowania w czasie kompilacji dla pewnego zestawu parametrów, ale nie dla wszystkich. Nie wymaga od kompilatorów diagnozowania constexpr
funkcji wykonującej pewne rzeczy, które w pewnych okolicznościach mogą nie być kompilowane, ani nawet tego, czy taka funkcja ma taki zestaw parametrów. Pozwala to uniknąć konieczności rozwiązywania problemu zatrzymania.