Próbuję obliczyć długość literału ciągu w czasie kompilacji. Aby to zrobić, używam następującego kodu:
#include <cstdio>
int constexpr length(const char* str)
{
return *str ? 1 + length(str + 1) : 0;
}
int main()
{
printf("%d %d", length("abcd"), length("abcdefgh"));
}
Wszystko działa zgodnie z oczekiwaniami, program wypisuje 4 i 8. Kod asemblera wygenerowany przez clang pokazuje, że wyniki są obliczane w czasie kompilacji:
0x100000f5e: leaq 0x35(%rip), %rdi ; "%d %d"
0x100000f65: movl $0x4, %esi
0x100000f6a: movl $0x8, %edx
0x100000f6f: xorl %eax, %eax
0x100000f71: callq 0x100000f7a ; symbol stub for: printf
Moje pytanie: czy standard gwarantuje, że length
funkcja będzie oceniana w czasie kompilacji?
Jeśli to prawda, drzwi do obliczeń literałów ciągów w czasie kompilacji właśnie otworzyły się dla mnie ... na przykład mogę obliczyć skróty w czasie kompilacji i wiele więcej ...
<cstdio>
a następnie dzwonienie::printf
nie jest przenośne. Norma wymaga jedynie<cstdio>
podaniastd::printf
.printf
może prowadzić do znacznie mniejszej ilości kodu do obsługi.Odpowiedzi:
Nie ma gwarancji, że wyrażenia stałe zostaną ocenione w czasie kompilacji, mamy tylko nienormatywny cytat z wersji roboczej C ++ sekcji standardowej
5.19
Wyrażenia stałe, który mówi tak:Możesz przypisać wynik do
constexpr
zmiennej, aby upewnić się, że jest ona oceniana w czasie kompilacji, możemy to zobaczyć z referencji C ++ 11 Bjarne Stroustrupa, która mówi ( wyróżnienie moje ):Na przykład:
constexpr int len1 = length("abcd") ;
Bjarne Stroustrup podaje podsumowanie, kiedy możemy zapewnić ocenę czasu kompilacji w tym wpisie na blogu isocpp i mówi:
To przedstawia dwa przypadki, w których powinno się to oceniać w czasie kompilacji:
shall be ... converted constant expression
lubshall be ... constant expression
jest używana, na przykład w powiązaniu z tablicą.constexpr
jak opisałem powyżej.źródło
constexpr int x = 5;
, że nie wymaga wartości w czasie kompilacji (zakładając, że nie jest używany jako parametr szablonu lub cokolwiek innego) i faktycznie emituje kod, który oblicza wartość początkową w czasie wykonywania przy użyciu 5 bezpośrednich wartości 1 i 4 dodatkowych operacji. Bardziej realistyczny przykład: kompilator może osiągnąć limit rekursji i odroczyć obliczenia do czasu wykonania. O ile nie zrobisz czegoś, co zmusi kompilator do rzeczywistego użycia wartości, „gwarantowana ocena w czasie kompilacji” jest problemem QOI.constexpr
obliczenia z czystego zła. Można nawet poczekać 1 sekundę na znak w danej linii źródła lub wziąć daną linię źródła i użyć jej do rozstawienia pozycji szachowej, a następnie zagrać w obie strony, aby określić, kto wygrał.Naprawdę łatwo jest dowiedzieć się, czy wywołanie
constexpr
funkcji powoduje powstanie podstawowego wyrażenia stałego, czy tylko jest optymalizowane:Użyj go w kontekście, w którym wymagane jest stałe wyrażenie.
int main() { constexpr int test_const = length("abcd"); std::array<char,length("abcdefgh")> test_const2; }
źródło
-pedantic
, jeśli używasz gcc. W przeciwnym razie nie otrzymasz żadnych ostrzeżeń ani błędówenum { Whatever = length("str") }
?static_assert(length("str") == 3, "");
constexpr auto test = /*...*/;
jest prawdopodobnie najbardziej ogólna i prosta.Tylko uwaga, że nowoczesne kompilatory (takie jak gcc-4.x) robią
strlen
dla literałów łańcuchowych w czasie kompilacji, ponieważ jest zwykle definiowana jako funkcja wewnętrzna . Bez włączonej optymalizacji. Chociaż wynik nie jest stałą czasową kompilacji.Na przykład:
printf("%zu\n", strlen("abc"));
Prowadzi do:
movl $3, %esi # strlen("abc") movl $.LC0, %edi # "%zu\n" movl $0, %eax call printf
źródło
strlen
jest to funkcja wbudowana, jeśli użyjemy-fno-builtins
jej , powrócimy do wywoływania jej w czasie wykonywania, zobacz to na żywostrlen
jestconstexpr
dla mnie, nawet z-fno-nonansi-builtins
(wygląda na to,-fno-builtins
że już nie istnieje w g ++). Mówię „constexpr”, bo mogę to zrobićtemplate<int> void foo();
ifoo<strlen("hi")>();
g ++ - 4.8.4Pozwólcie, że zaproponuję inną funkcję, która oblicza długość łańcucha w czasie kompilacji bez rekurencji.
template< size_t N > constexpr size_t length( char const (&)[N] ) { return N-1; }
Spójrz na ten przykładowy kod w ideone .
źródło
char temp[256]; sprintf(temp, "%u", 2); if(1 != length(temp)) printf("Your solution doesn't work");
ideone.com/IfKUHVNie ma gwarancji, że
constexpr
funkcja jest oceniana w czasie kompilacji, chociaż każdy rozsądny kompilator wykona to na odpowiednich, włączonych poziomach optymalizacji. Z drugiej strony parametry szablonu muszą być oceniane w czasie kompilacji.Użyłem następującej sztuczki, aby wymusić ocenę w czasie kompilacji. Niestety działa tylko z wartościami całkowitymi (tj. Nie z wartościami zmiennoprzecinkowymi).
template<typename T, T V> struct static_eval { static constexpr T value = V; };
Teraz, jeśli napiszesz
if (static_eval<int, length("hello, world")>::value > 7) { ... }
możesz być pewien, że
if
instrukcja jest stałą czasu kompilacji bez narzutu czasu wykonywania.źródło
len
bycieconstexpr
środkiem i taklength
musi zostać ocenione w czasie kompilacji.if
warunek -warunek (w którym kompilator usunął martwy kod), dla którego pierwotnie użyłem sztuczki.Krótkie wyjaśnienie z wpisu Wikipedii o uogólnionych wyrażeniach stałych :
Umieszczenie
constexpr
słowa kluczowego przed definicją funkcji instruuje kompilator, aby sprawdzić, czy te ograniczenia są spełnione. Jeśli tak, a funkcja jest wywoływana ze stałą, gwarantuje się, że zwracana wartość będzie stała, a zatem może być używana wszędzie tam, gdzie wymagane jest wyrażenie stałe.źródło