5 lutego 2013

Deklaracja zmiennych i trochę o const/constexpr

Najpierw małe przypomnienie, jak wygląda deklaracja zmiennych w C++. Rzadko stosuję i spotykam deklaracje zmiennych jakiegoś typu po przecinku, stąd czytając książkę, sam byłem zaskoczony, co tak naprawdę znaczył ten kod.
int i, *ip = nullptr; // i jest typu int, ip jest wskaźnikiem na int.
int* p1, p2;          // p1 jest wskaźnikiem na int, p2 jest typu int.
int* a, &b = *ip;     // a jest wskaźnikiem na int, b jest referencją na wartość 
                      // wskazywaną przez ip.
A teraz trochę o const. Zawsze jest to dla mnie mylące, przynajmniej po dłuższym okresie czasu, gdy wiedza powolutku wyparowuje.
const int c = 8;  // c jest stałą typu int, jego wartość nie może być zmieniona.
const int &r = c; // ok, referencja na stały obiekt typu int - mówimy co będziemy mogli 
                  // robić przy  pomocy referencji - odwołując się do r nie damy rady 
                  // zmodyfikować c (chociaż sam w sobie nie jest const).
int &r2 = c;      // error, referencja na niestały obiekt.
Nie ma czegoś takiego jak stałe referencje (z technicznego punktu widzenia), są tylko referencje na stałe. Ponieważ nie można zmusić referencji by wskazywała na inny obiekt w jakimś sensie wszystkie referencje są stałe.
double dval = 3.14;
const double *pt = &dval; // ok (tak jak wyżej), ale nie możemy zmieniać dval, przy pomocy pt
A teraz co powstanie, gdy const znajdzie się w różnych miejscach? Pewne oznaczenia - cytując za książką:
Używamy terminu top-level const do oznaczenia, że sam wskaźnik jest stały. Kiedy wskaźnik może wskazywać na stały obiekt, odnosimy się do tego jako low-level const
int i = 0;
const int c1 = 88;        // c1 jest stałym obiektem typu int
int const c2 = 99;        // (TO SAMO) c2 jest stałym obiektem typu int

int *const p1 = &i;       // p1 jest stałym wskaźnikiem na obiekt typu int (top-level const).
const int ci = 42;        // ci jest stałym obiektem typu int (top-level const).
const int *p2 = &ci;      // p2 jest wskaźnikiem na stały obiekt typu int (low-level).
int const *p3 = &ci;      // (TO SAMO) p3 jest wskaźnikiem na stały obiekt typu int.
const int *const p4 = p2; // p3 jest stałym wskaźnikiem na stały obiekt typu int
                          // (prawy const jest top-level; lewy nie)
const int &r = i;         // referencja na stały obiekt typu int.
                          // const w typach referencyjnych zawsze jest low-level.
W odróżnieniu od referencji, wskaźniki są obiektami, więc mogą być const, co znaczy, że po ustawieniu w momencie inicjalizacji nie mogą na nic innego wskazywać.

A teraz o nowościach. const expression jest wyrażeniem, którego wartość nie może zostać zmieniona, i dla którego wartość powinna być (ale nie musi) obliczona podczas kompilacji. W C++11 pojawia się nowy modyfikator constexpr, który zbada, czy tak rzeczywiście się stanie. Funkcje z constexpr muszą być na tyle proste by kompilator poradził sobie z nimi podczas kompilacji, ale w kolejnych wersjach standardu konstrukcje będą stawać się coraz bardziej skomplikowane (pętle, algorytmy STL, kontenery itp.). W zamyśle constexpr ma stać się alternatywą dla metaprogramowania w szablonach.
#include <iostream>
#include <stdio.h>

using namespace std;

constexpr int get_size()
{
    return 6;
}

int main()
{
    constexpr int sz = get_size() + 80; // ok, pod warunkiem, że funkcja też jest constexpr.
    printf("%d", sz);
    return 0;
}
I podgląd tego co wyprodukował kompilator (g++ -std=c++17 -S -masm=intel main.cpp). Jak widać w pliku binarnym pojawia się już obliczona wartość (80+6=86).
main:
.LFB1824:
    .cfi_startproc
    endbr64
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov     rbp, rsp
    .cfi_def_cfa_register 6
    sub     rsp, 16
    mov     DWORD PTR -4[rbp], 86
    mov     esi, 86
    lea     rdi, .LC0[rip]
    mov     eax, 0
    call    printf@PLT
    mov     eax, 0
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

Brak komentarzy:

Prześlij komentarz