Pytanie Czy constexpr jest "podpowiedź" (jak inline) lub "żądanie wiązania" do kompilatora?


Jest constexpr wskaźnik dla kompilatora lub czy nakazuje zachowanie?

Poniższy przykład przedstawia się następująco:

template<typename T> 
std::size_t constexpr getID() { return typeid(T).hash_code(); }

hash_code to stała czasu wykonywania, ale ten fragment byłby skompilować mimo że zażądano oceny czasu kompilacji constexpr. Dopiero po użyciu zwracanej wartości, gdy oczekiwana jest stała czasu kompilacji, zauważylibyśmy, że to nie może być użyty jako funkcja constexpr.

Więc jest constexpr "podpowiedź" (podobnie jak inline słowo kluczowe) lub "żądanie wiązania" do kompilatora?


18
2018-05-21 06:37


pochodzenie


Zauważ, że ponieważ jest to szablon, istnieje nie jest żadną funkcją dopóki nie utworzysz tego z niektórymi T.
W tym przykład, widzisz wystąpienia tej kompilacji funkcji - Nikos Athanasiou
@Mehrdad thnx za korektę - Nikos Athanasiou
inline też nie jest wskazówką. - CB Bailey
@rubenvb: "tak wydajny, jak to możliwe" jest bezsensownym (dobrze, QOI) gofrem, ale zmienia się zasada ODR (prawdziwe żyłki inline does) są obowiązkową zmianą zachowania (przynajmniej w C ++, o czym mówiłem w moim oryginalnym komentarzu). - CB Bailey


Odpowiedzi:


Od Strona Wiki C ++ 11:

Jeśli funkcja constexpr lub konstruktor zostanie wywołana z argumentami, które   nie są wyrażeniami stałymi, wywołanie zachowuje się tak, jakby funkcja była   not constexpr, a wynikowa wartość nie jest wyrażeniem stałym.   Podobnie, jeśli wyrażenie w instrukcji return elementu constexpr   funkcja nie ocenia stałego wyrażenia dla konkretu   wywołanie, wynik nie jest wyrażeniem stałym.

The constexpr Specifier wyraża w ten sposób możliwość ocenić coś podczas kompilacji i podlega pewne ograniczenia kiedy jest używany.


Dla danego fragmentu wydaje mi się, że ograniczenie C ++ 11:

dokładnie jedna instrukcja return, która zawiera tylko wartości literalne,    Zmienne i funkcje constexpr

nie jest spełniony, ponieważ hash_code jest zdefiniowany jako:

size_t hash_code() const;

W tym przypadku standardowy projekt n3242 mówi:

Dla funkcji constexpr, jeśli nie istnieją żadne wartości argumentów funkcji   że podstawienie wywołania funkcji da stałą   wyrażenie (5.19), program jest źle sformułowany; diagnostyka nie jest wymagana.

Wierzę, że twój przykład pasuje tutaj.


7
2018-05-21 06:45



Więc w zasadzie constexpr był bezsensownym dodatkiem do języka. - Mehrdad
@Mehrdad: Wcale nie. Nadal można wywoływać funkcje constexpr przy wartościach stałych czasowych kompilacji i mogą one być analizowane również w czasie kompilacji. - usr1234567
@ user2799037: Nie chodzi o to, że mogą być oceniane w czasie kompilacji, ale mogą być używane dla rzeczy, które akceptują tylko wyrażenia stałe, takie jak parametry szablonów lub rozmiary tablic. - Jan Hudec
@ user2799037: er, nie powiedziałem, że to bezużyteczne słowo kluczowe. Jest tam, ponieważ ma zastosowanie. Powiedziałem, że nie ma potrzeby dodawania go do języka na początku; mogli to osiągnąć inaczej. Mogliby zamiast tego po prostu stwierdzić, że każde wywołanie funkcji jest oceniane podczas kompilacji, jeśli to możliwe (tj. Jeśli wynik byłby taki sam jak w czasie wykonywania), co sprawia, że ​​niepotrzebne jest posiadanie słowa kluczowego tylko w tym celu. - Mehrdad
@ Jens: Próba oceny wszystkich funkcji w czasie wykonania dałaby taki sam wynik: Jeśli używasz go tam, gdzie musi być oceniony podczas kompilacji i nie może być oceniany podczas kompilacji ... dostaniesz błąd. Więc nie potrzeba słowa kluczowego ... tak jak mówi Mehrdad: to bezcelowe! - mmmmmmmm


Jest constexpr "podpowiedź" (np. inline) lub "żądanie wiązania" do kompilatora?

To nie jest ani. Zapomnij o gdy jest oceniany. Wszystko (z kilkoma małymi wyjątkami, w szczególności z udziałem volatile) jest oceniany za każdym razem, gdy kompilator uzna to za konieczne do wygenerowania zachowania maszyny abstrakcyjnej w C ++. Nie ma o czym mówić gdy rzeczy są oceniane.

Kompilator może tworzyć kod, który oceniałby, co stałoby się wyrażeniami w środowisku wykonawczym, jeśli nie powoduje to innego zachowania. Można tworzyć kod oceniający rzeczy nie oznaczone constexpr w czasie kompilacji, jeśli ma spryt.

Jeśli nie chodzi o czas kompilacji a czas wykonania, to co jest constexpr o, to?

constexpr pozwala na traktowanie rzeczy jako stałych wyrażeń. Wszystko oznaczone constexpr  musi mieć możliwość wytworzenia stałej ekspresji w jakiś sposób.

W przypadku funkcji mogą być w stanie wytworzyć wyrażenia stałe z pewnymi argumentami, ale nie z innymi. Ale dopóki istnieje zestaw argumentów, które mogą powodować stałe wyrażenie, można oznaczyć funkcję constexpr. Jeśli taki zestaw argumentów jest używany w wywołaniu funkcji, to wyrażenie jest wyrażeniem stałym. Czy to oznacza, że ​​jest oceniany podczas kompilacji? Patrz wyżej. Jest oceniany, gdy kompilator uzna to za stosowne. Jedyne, co oznacza to, że możesz go używać w kontekście wymagającym stałego wyrażenia.

W przypadku zmiennych albo są one wyrażeniami stałymi, albo nie. Nie mają żadnych argumentów, więc jeśli constexpr zawsze muszą być inicjowane ciągłymi wyrażeniami.

TL; DR: constexpr polega na oznaczaniu rzeczy jako użytecznych w wyrażeniach ciągłych, a nie na decydowaniu, kiedy je oceniać.


Po odejściu z drogi wydaje się, że twój szablon funkcji jest źle sformułowany. Nie ma zestawu argumentów, które mogłyby spowodować stałą ekspresję. Standard nie wymaga jednak diagnostyki.


14
2018-05-21 09:08



Zobacz przykładowe wyjaśnienie funkcji, która może wytwarzać zarówno wyrażenia stałe, jak i wyrażenia niestałe, nawet jeśli wszystkie argumenty są wyrażeniami stałymi: stackoverflow.com/a/13039987/46642. - R. Martinho Fernandes
Either the compiler is buggy, or this is a compiler extension : jeżeli standard nie zmienił się z n3242, to wyraźnie stwierdzono, że dla tego rodzaju błędu nie jest wymagana diagnostyka - Massimiliano
@Massimiliano dzięki. - R. Martinho Fernandes


Funkcje constexpr mogą być używane do oceny stałych czasowych kompilacji. Więc można go używać jak:

 constexpr int func(int a) { return a+2; }

 char x[func(10)];

Jeśli func zostanie wywołany podczas wykonywania, kompilator mogą najpierw oceń to wyrażenie, jeśli to możliwe. Ale nie jest to koniecznością, ale zwykle wykonywane, jeśli wejście jest również const.

Ważne jest również posiadanie konstruktorów constexpr. Jest to jedyna szansa na pozyskanie obiektów niezwiązanych z klasy const.

    class Point
    {   
        private:    
            int x;
            int y;
        public:
            constexpr Point( int _x, int _y) : x(_x), y(_y) {}  
            constexpr int GetX() const { return x; }
    };  

    constexpr Point p{1,2};

    int main()
    {   

        char i[p.GetX()];
        return 0;
    }   

2
2018-05-21 07:18



Czy możesz rozwinąć swój punkt dotyczący klas nie-POD? Jest const std::string hello("hello"); ani const wystąpienie klasy non-POD? - CB Bailey
przepraszam za złe sformułowanie. mam na myśli constexpr obiekty! Zobacz przykład dodany! - Klaus
Z typem złożonym można tworzyć obiekty inne niż POD w wyrażeniach stałych bez konstruktora (agregaty nie mogą mieć konstruktorów). Ważność nie zależy od PODST. - R. Martinho Fernandes


Pełna odpowiedź na twoje pytanie ma dwa aspekty:

  1. Funkcja constexpr może być oceniona tylko podczas kompilacji, kiedy wszystko argumenty mogą być oceniane podczas kompilacji. Może nadal być używany jako normalna funkcja, która jest oceniana w czasie wykonywania.

  2. Zmienna constexpr musi zostać zainicjowana wartością obliczoną podczas kompilacji. Kompilator musi zgłosić błąd, jeśli nie może tego zrobić.

Można przypisać kod skrótu do zmiennej constexpr, a następnie uzyskać dane wyjściowe kompilatora:

#include <typeinfo>
#include <array>

template<typename T> 
std::size_t constexpr getID() { 

    return []() {constexpr size_t id = typeid(T).hash_code(); return id;}(); }

int main() {
    // both statement generate compiler errors
    //std::array<int, typeid(int).hash_code()> a;
    //constexpr size_t y = typeid(int).hash_code();

    size_t x = getID<int>();
}

1
2018-05-21 07:46



Co jest zainicjalizowana wartość? Są tam niezainicjowane wartości? - dyp
@dyp int i będzie niezainicjowaną wartością, ponieważ programista nie zainicjował jej jawnie. Czy jest na to lepsze słowo? Może zainicjowana zmienna constexpr? - Jens
Nie jest jasne, jaka jest wartość (o ile wiem), ale zazwyczaj mówisz int i; że i to nazwa obiektu (obiekt to region pamięci) i który sklepy wartość. Więc niezainicjowana zmienna lub niezainicjowany obiekt powinno być dobrze. IIRC, w C ++ 1y, otrzymamy pojęcie o nieokreślona wartość; int x; brak inicjalizacji miałby (zawiera) nieokreśloną wartość. - dyp