Obliczanie długości ciągu C w czasie kompilacji. Czy to naprawdę constexpr?

Próbuję obliczyć długość ciągu znaków w czasie kompilacji. W tym celu 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 wyświetla 4 i 8. Kod zestawu generowany 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 funkcja length będzie oceniana w czasie kompilacji?

Jeśli to prawda, drzwi do obliczeń literałów w czasie kompilacji tylko otwarte dla mnie... na przykład mogę obliczać hasze w czasie kompilacji i wiele innych...

Author: Shafik Yaghmour, 2014-09-17

6 answers

Wyrażenia stałe nie są gwarantowane do oceny w czasie kompilacji, mamy tylko nienormatywny cytat z szkic standardu C++ sekcja 5.19 wyrażenia stałe {[10] } mówiące tak:

[...] > [Uwaga: wyrażenia stałe można oceniać podczas tłumaczenie.- Uwaga końcowa]

Możesz przypisać wynik do zmiennej constexpr aby mieć pewność, że jest ona oceniana podczas kompilacji, możemy to zobaczyć z Bjarne Stroustrup ' S C++11 reference który mówi ():

Oprócz możliwości oceny wyrażeń w czasie kompilacji, możemy chcesz być w stanie wymagać wyrażenia do oceny przy kompilacji czasu; constexpr przed definicją zmiennej robi to (i implikuje const):

Na przykład:

constexpr int len1 = length("abcd") ;

Bjarne Stroustrup podsumowuje, kiedy możemy zapewnić ocenę czasu kompilacji w tym wpisie isocpp blog i mówi:

[...] Prawidłowej odpowiedzi - jak stwierdzono przez Herba-jest to, że zgodnie ze standardem funkcja constexpr może być oceniane w czasie kompilatora lub w czasie uruchomienia, chyba że jest używany jako wyrażenie stałe, w którym to przypadku musi być obliczone na czas kompilacji. Aby zagwarantować ocenę w czasie kompilacji, musimy albo użyć tam, gdzie wymagane jest wyrażenie stałe (np. jako Wiązanie tablicy lub jako etykieta przypadku) lub użyć jej do zainicjowania constexpr. Mam nadzieję, że że żaden szanujący się kompilator nie przegap optymalizacji możliwość zrobienia tego, co pierwotnie powiedziałem: "funkcja constexpr jest obliczany w czasie kompilacji, jeśli wszystkie jego argumenty są stałe wyrażenia."

Więc przedstawia dwa przypadki, w których powinna być oceniana w czasie kompilacji:

  1. użyj go tam, gdzie wymagane jest stałe wyrażenie, wydaje się, że jest to gdziekolwiek w projekcie standardu, gdzie używane jest wyrażenie shall be ... converted constant expression lub shall be ... constant expression, takie jak Wiązanie tablicy.
  2. użyj go, aby zainicjować constexpr, ponieważ zarys powyżej.
 63
Author: Shafik Yaghmour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-18 04:18:06

Bardzo łatwo jest dowiedzieć się, czy wywołanie funkcji constexpr powoduje wyrażenie stałej rdzenia czy jest tylko optymalizowane:

Użyj go w kontekście, w którym wymagane jest wyrażenie stałe.

int main()
{
    constexpr int test_const = length("abcd");
    std::array<char,length("abcdefgh")> test_const2;
}
 24
Author: Ben Voigt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-17 12:49:56

Tylko Uwaga, że nowoczesne Kompilatory (jak gcc-4.x) do strlen dla literałów łańcuchowych w czasie kompilacji, ponieważ jest normalnie zdefiniowana jako funkcja wewnętrzna . Bez włączonych optymalizacji. Chociaż wynik nie jest stałą czasową kompilacji.

Np.:

printf("%zu\n", strlen("abc"));

Wyniki w:

movl    $3, %esi    # strlen("abc")
movl    $.LC0, %edi # "%zu\n"
movl    $0, %eax
call    printf
 14
Author: Maxim Egorushkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 14:12:55

Pozwól mi zaproponować inną funkcję, która oblicza długość ciągu znaków w czasie kompilacji bez bycia rekurencyjnym.

template< size_t N >
constexpr size_t length( char const (&)[N] )
{
  return N-1;
}
Zobacz przykładowy kod ideone.
 11
Author: user2436830,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-28 06:56:06

Nie ma gwarancji, że funkcja constexpr jest oceniana w czasie kompilacji, chociaż każdy rozsądny kompilator zrobi to na odpowiednim poziomie optymalizacji. Z drugiej strony parametry szablonu muszą być oceniane podczas kompilacji.

Użyłem poniższej sztuczki, aby wymusić ocenę w czasie kompilacji. Niestety działa tylko z wartościami całkowitymi (tzn. 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 pracy.

 6
Author: 5gon12eder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-17 20:49:08

Krótkie wyjaśnienie z wpisu Wikipedii na temat uogólnionych wyrażeń stałych :

Użycie constexpr na funkcji nakłada pewne ograniczenia na to, co ta funkcja może zrobić. Po pierwsze, funkcja musi mieć typ powrotu non-void. Po drugie, ciało funkcji nie może deklarować zmiennych ani definiować nowych typów. Po trzecie, jednostka może zawierać tylko deklaracje, deklaracje null i pojedyncze oświadczenie return. Musi istnieć wartość argumentu taka, że po podstawieniu argumentu, wyrażenie w instrukcji return tworzy wyrażenie stałe.

Posiadanie słowa kluczowego constexpr przed definicją funkcji nakazuje kompilatorowi sprawdzenie, czy te ograniczenia są spełnione. Jeśli tak, a funkcja jest wywoływana ze stałą, zwracana wartość jest gwarantowana jako stała i dlatego może być używana wszędzie tam, gdzie wymagane jest wyrażenie stałe.

 1
Author: kaedinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-19 00:43:46