Kiedy należy używać funkcji constexpr w C ++ 11?


337

Wydaje mi się, że posiadanie „funkcji, która zawsze zwraca 5”, łamie lub osłabia znaczenie „wywoływania funkcji”. Musi być jakiś powód lub potrzeba takiej możliwości, inaczej nie byłoby w C ++ 11. Dlaczego tam jest

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

Wydaje mi się, że gdybym napisał funkcję, która zwraca wartość dosłowną, i podszedłbym do recenzji kodu, ktoś powiedziałby mi, że powinienem zadeklarować stałą wartość zamiast pisać return 5.


28
Czy potrafisz zdefiniować funkcję rekurencyjną, która zwraca a constexpr? Jeśli tak, widzę użycie.
ereOn

20
Uważam, że pytanie powinno brzmieć „po co wprowadzać nowe słowo kluczowe (!), Jeśli kompilator może sam wydedukować, czy można ocenić funkcję w czasie kompilacji, czy nie”. „Gwarantowane przez słowo kluczowe” brzmi dobrze, ale myślę, że wolę mieć gwarancję, gdy tylko jest to możliwe, bez potrzeby używania słowa kluczowego.
Kos

6
@Kos: Ktoś, kto jest WIĘCEJ zaznajomiony z wewnętrznymi elementami C ++, prawdopodobnie wolałby twoje pytanie, ale moje pytanie pochodzi z perspektywy osoby, która wcześniej napisała kod C, ale w ogóle nie zna słów kluczowych C ++ 2011, ani szczegółów implementacji kompilatora C ++ . Zdolność rozumowania na temat optymalizacji kompilatora i dedukcji ciągłych wyrażeń jest przedmiotem bardziej zaawansowanego pytania użytkownika niż to.
Warren P,

8
@Kos myślałem w ten sam sposób co ty, a odpowiedź, na którą wpadłem, brzmiała: bez constexpr, skąd (łatwo) mógłbyś wiedzieć, że kompilator faktycznie ocenił dla ciebie tę funkcję? Podejrzewam, że mógłbyś sprawdzić dane wyjściowe zestawu, aby zobaczyć, co on zrobił, ale łatwiej jest po prostu powiedzieć kompilatorowi, że potrzebujesz tej optymalizacji, a jeśli z jakiegoś powodu nie może tego zrobić dla ciebie, da ci ładną kompilację. błąd zamiast cichego braku optymalizacji tam, gdzie się spodziewałeś.
Jeremy Friesner

3
@ Ko: Można powiedzieć to samo const. W rzeczywistości mandat jest użyteczny ! Wymiary tablicy są kanonicznym przykładem.
Wyścigi lekkości na orbicie

Odpowiedzi:


303

Załóżmy, że robi to coś bardziej skomplikowanego.

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

Teraz masz coś, co można ocenić do stałej, zachowując dobrą czytelność i pozwalając na nieco bardziej złożone przetwarzanie niż tylko ustawienie stałej na liczbę.

Zasadniczo zapewnia dobrą pomoc w utrzymaniu, ponieważ staje się bardziej oczywiste, co robisz. Weźmy max( a, b )na przykład:

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

Jest to dość prosty wybór, ale oznacza to, że jeśli wywołujesz maxze stałymi wartościami, jest to jawnie obliczane w czasie kompilacji, a nie w czasie wykonywania.

Kolejnym dobrym przykładem może być DegreesToRadiansfunkcja. Wszyscy uważają stopnie za łatwiejsze do odczytania niż radiany. Chociaż możesz wiedzieć, że 180 stopni jest w radianach, jest o wiele wyraźniej napisane w następujący sposób:

const float oneeighty = DegreesToRadians( 180.0f );

Wiele dobrych informacji tutaj:

http://en.cppreference.com/w/cpp/language/constexpr


18
Doskonały punkt, ponieważ informuje kompilator, aby spróbował obliczyć wartość w czasie kompilacji. Ciekawe, dlaczego const nie zapewnia tej funkcji, gdy określono konkretne optymalizacje? A może to?
TamusJRoyce,

11
@Tamus: Często tak będzie, ale nie jest do tego zobowiązany. constexpr zobowiązuje kompilator i wypluje błąd, jeśli nie może.
Goz

20
Teraz to widzę. Grzech (0,5) jest kolejnym. To porządnie zastępuje makra C.
Warren P

10
Widzę to jako nowe pytanie do wywiadu: Wyjaśnij różnice między słowem kluczowym const i constexpr.
Warren P

2
Jako sposób na samodzielne udokumentowanie tego punktu napisałem podobny kod jak powyżej, przy czym funkcja to „const” zamiast „constexpr”. Gdy używam Clang3.3, -pedantic-error i -std = c ++ 11 spodziewałem się, że ten ostatni się nie skompiluje. Skompilował się i działał jak w przypadku „constexpr”. Czy podejrzewasz, że jest to rozszerzenie clang, czy poprawiono specyfikację C ++ 11 od czasu odpowiedzi na ten post?
Arbalest

144

Wprowadzenie

constexprnie został wprowadzony jako sposób poinformowania implementacji, że coś można ocenić w kontekście, który wymaga stałej ekspresji ; implementacje zgodne były w stanie to udowodnić przed C ++ 11.

Coś, czego implementacja nie może udowodnić, jest intencją określonego fragmentu kodu:

  • Co deweloper chce wyrazić za pomocą tego podmiotu?
  • Czy powinniśmy ślepo pozwolić, aby kod był używany w wyrażeniu stałym , tylko dlatego, że tak się dzieje?

Czym byłby świat bez constexpr?

Załóżmy, że tworzysz bibliotekę i zdajesz sobie sprawę, że chcesz być w stanie obliczyć sumę każdej liczby całkowitej w przedziale (0,N].

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

Brak zamiaru

Kompilator może łatwo udowodnić, że powyższą funkcję można wywołać w wyrażeniu stałym jeśli przekazany argument jest znany podczas tłumaczenia; ale nie zadeklarowaliście tego jako zamiaru - tak się po prostu stało.

Teraz pojawia się ktoś inny, czyta twoją funkcję, wykonuje taką samą analizę jak kompilator; „ Och, ta funkcja nadaje się do wyrażenia ciągłego!” i zapisuje następujący fragment kodu.

T arr[f(10)]; // freakin' magic

Optymalizacja

Ty, jako „niesamowity” programista bibliotek, decydujesz, że fpowinieneś buforować wynik podczas wywoływania; kto chciałby ciągle obliczać ten sam zestaw wartości?

int func (int n) { 
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ()) 
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

Wynik

Wprowadzając swoją głupią optymalizację, po prostu przerwałeś każde użycie funkcji, które zdarzyło się w kontekście ciągłego wyrażania wymagane było .

Nigdy nie obiecałeś, że funkcja będzie użyteczna w ciągłym wyrażaniu i bez constexprniej nie byłoby sposobu na zapewnienie takiej obietnicy.


Dlaczego więc potrzebujemy constexpr?

Podstawowym zastosowaniem constexpr jest deklarowanie zamiaru .

Jeśli jednostka nie jest oznaczona jako constexpr- nigdy nie była przeznaczona do użycia w wyrażeniu stałym ; a nawet jeśli tak jest, polegamy na kompilatorze w diagnozowaniu takiego kontekstu (ponieważ nie uwzględnia on naszych zamiarów).


25
Jest to prawdopodobnie poprawna odpowiedź, ponieważ ostatnie zmiany w C ++ 14 i C ++ 17 pozwalają na użycie znacznie szerszego zakresu języka w constexprwyrażeniach. Innymi słowy, prawie wszystko może być opatrzone adnotacjami constexpr(może kiedyś po prostu odejdzie z tego powodu?) I chyba że ktoś ma kryterium, kiedy użyć, constexprczy nie, prawie cały kod zostanie napisany jako taki .
alecov

4
@alecov Zdecydowanie nie wszystko ... I/O, syscalla dynamic memory allocationzdecydowanie cann't być oznaczone jako constexprZresztą nie wszystko powinno być constexpr.
JiaHao Xu

1
@alecov Niektóre funkcje mają być wykonywane w czasie wykonywania i nie ma sensu robić tego w czasie kompilacji.
JiaHao Xu,

1
Podoba mi się również ta odpowiedź. Ocena czasu kompilacji jest zgrabną optymalizacją, ale to, co naprawdę zyskujesz, constexprjest gwarancją pewnego rodzaju zachowania. Tak jak constrobi.
Tomáš Zato - Przywróć Monikę

Jaki kompilator pozwala tej kompilacji, która nie zawiera constexpr, int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)]; nie mogę jej nigdzie skompilować?
Jer

91

Weź std::numeric_limits<T>::max(): z jakiegokolwiek powodu, jest to metoda. constexprprzydałoby się tutaj.

Kolejny przykład: chcesz zadeklarować tablicę C (lub a std::array), która jest tak duża jak inna tablica. Obecnie można to zrobić w następujący sposób:

int x[10];
int y[sizeof x / sizeof x[0]];

Ale czy nie byłoby lepiej móc pisać:

int y[size_of(x)];

Dzięki temu constexprmożesz:

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}

1
+1 za dobre użycie szablonu, ale działałoby dokładnie tak samo bez constexpr, prawda?
Kos

21
@ Ko: Nie. Zwróciłoby to wartość czasu wykonywania. constexprwymusza na kompilatorze, aby funkcja zwracała wartość czasu kompilacji (jeśli to możliwe).
deft_code

14
@Kos: bez tego constexprnie można go użyć w deklaracji rozmiaru tablicy ani jako argumentu szablonu, niezależnie od tego, czy wynikiem wywołania funkcji jest stała czasu kompilacji, czy nie. Te dwa są w zasadzie jedynymi przypadkami użycia, constexprale przynajmniej szablonowy przypadek użycia jest dość ważny.
Konrad Rudolph

2
„z jakiegokolwiek powodu, jest to metoda”: Powodem jest to, że w C ++ 03 istnieją tylko liczby całkowite czasu kompilacji, ale nie ma innych typów czasu kompilacji, więc tylko metoda może działać dla dowolnych typów przed C ++ 11.
Sebastian Mach

5
@LwCui Nie, to nie jest „ok”: GCC domyślnie jest po prostu niepewny co do pewnych rzeczy. Użyj -pedanticopcji, a zostanie ona oznaczona jako błąd.
Konrad Rudolph,

19

constexprfunkcje są naprawdę ładne i stanowią doskonały dodatek do c ++. Masz jednak rację, że większość problemów, które rozwiązuje, można nieelegancko obejść za pomocą makr.

Jednak jedno z zastosowań constexprnie ma odpowiedników w typie C ++ 03.

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;

12
Czy możesz wyjaśnić, że „BARDZO subtelny błąd linkera”? A przynajmniej zapewnić wskaźnik do wyjaśnienia?
enobayram

4
@ enobayram, operator trójskładnikowy przyjmuje adres operandów. Nie wynika to z kodu. Wszystko kompiluje się dobrze, ale link nie działa, ponieważ adres fournie jest rozpoznawany. Musiałem naprawdę wykopać, kto bierze adres mojej static constzmiennej.
deft_code

23
„To jest złe z oczywistych powodów”: najbardziej oczywistym powodem jest średnik, prawda?
TonyK

4
„BŁĘDNIE subtelny błąd linkera” całkowicie mnie zaskoczył. Ani fournie fivesą objęte zakresem.
Steven Lu,

3
zobacz także nowy enum classtyp, naprawia niektóre problemy wyliczania.
ninMonkey

14

Z tego, co przeczytałem, potrzeba constexpr wynika z problemu w metaprogramowaniu. Klasy cech mogą mieć stałe reprezentowane jako funkcje, pomyśl: numeric_limits :: max (). Dzięki constexpr tego typu funkcje mogą być używane w metaprogramowaniu lub jako ograniczenia tablic itp.

Innym przykładem z góry mojej głowy jest to, że dla interfejsów klasowych możesz chcieć, aby typy pochodne definiowały własne stałe dla niektórych operacji.

Edytować:

Po wywiercenie na tak, to wygląda na to inni mają wymyślić kilka przykładów tego, co może być możliwe z constexprs.


„Aby być częścią interfejsu, musisz być funkcją”?
Daniel Earwicker

Teraz, gdy widzę przydatność tego, jestem trochę bardziej podekscytowany C ++ 0x. To wydaje się dobrze przemyślaną rzeczą. Wiedziałem, że muszą. Ci standardowi uber-maniacy rzadko robią losowe rzeczy.
Warren P

Jestem o wiele bardziej podekscytowany lambdas, modelem wątków, listą inicjalizatora, referencjami do wartości, szablonami variadic, nowymi przeciążeniami wiązania ... jest na co czekać.
luke

1
O tak, ale już rozumiem lambdas / zamknięcia w kilku innych językach. constexprjest szczególnie przydatny w kompilatorze z potężnym systemem oceny wyrażeń w czasie kompilacji. C ++ naprawdę nie ma peerów w tej domenie. (to mocna pochwała dla C ++ 11, IMHO)
Warren P

11

Z przemówienia Stroustrupa na „Going Native 2012”:

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s’
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2’ 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human

2
Ten przykład można również znaleźć w artykule Stroustrup Software Development for Infrastructure .
Matthieu Poullet

clang-3.3: error: Zwracany typ funkcji constexpr „Wartość <druga>” nie jest literałem
Mitja

To miłe, ale kto umieszcza literały w takim kodzie. Posługiwanie się kompilatorem „sprawdzania jednostek” miałoby sens, gdybyś pisał interaktywny kalkulator.
bobobobo

5
@bobobobo lub jeśli pisałeś oprogramowanie nawigacyjne dla Mars Climate Orbiter, być może :)
Jeremy Friesner

1
Aby go skompilować - 1. W dosłownych przyrostkach użyj podkreślenia. 2. dodaj operator „” _m dla 100_m. 3. użyj 100.0_m lub dodaj przeciążenie, które akceptuje długo niepodpisane. 4. Zadeklaruj constexpr konstruktora wartości. 5. Dodaj odpowiedni operator / do klasy Value w następujący sposób: constexpr auto operator / (const Wartość <Y> i inne) const {return Wartość <Jednostka <TheUnit :: m - Wartość <Y> :: TheUnit :: m, TheUnit :: kg - Wartość <Y> :: TheUnit :: kg, TheUnit :: s - Wartość <Y> :: TheUnit :: s >> (val / other.val); }. Gdzie TheUnit jest wpisany jako Unit dodane wewnątrz klasy Value.
0kcats,

8

Innym zastosowaniem (jeszcze nie wspomnianym) są constexprkonstruktory. Umożliwia to tworzenie stałych czasowych kompilacji, które nie muszą być inicjowane podczas działania.

const std::complex<double> meaning_of_imagination(0, 42); 

Po sparowaniu z literałami zdefiniowanymi przez użytkownika uzyskasz pełne wsparcie dla literałów zdefiniowanych przez użytkownika.

3.14D + 42_i;

6

Kiedyś był metaprogramowanie:

template<unsigned T>
struct Fact {
    enum Enum {
        VALUE = Fact<T-1>*T;
    };
};

template<>
struct Fact<1u> {
    enum Enum {
        VALUE = 1;
    };
};

// Fact<10>::VALUE is known be a compile-time constant

Wierzę, że constexprzostał wprowadzony, aby umożliwić pisanie takich konstrukcji bez potrzeby korzystania z szablonów i dziwnych konstrukcji ze specjalizacją, SFINAE i innymi rzeczami - ale dokładnie tak, jakbyś pisał funkcję czasu wykonywania, ale z gwarancją, że wynik zostanie określony podczas kompilacji -czas.

Należy jednak pamiętać, że:

int fact(unsigned n) {
    if (n==1) return 1;
    return fact(n-1)*n;
}

int main() {
    return fact(10);
}

Skompiluj to, g++ -O3a zobaczysz, że fact(10)jest to ewaluowane w czasie kompilacji!

Kompilator obsługujący VLA (kompilator C w trybie C99 lub kompilator C ++ z rozszerzeniami C99) może nawet umożliwiać:

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

Ale w tej chwili jest to niestandardowy C ++ - constexprwygląda na to, że można to zwalczyć (nawet bez VLA, w powyższym przypadku). Nadal istnieje problem z koniecznością posiadania „formalnych” wyrażeń stałych jako argumentów szablonu.


Funkcja fakt nie jest oceniana w czasie kompilacji. Musi być constexpr i musi mieć tylko jedną instrukcję return.
Sumant,

1
@Sumant: Masz rację, że nie trzeba go oceniać w czasie kompilacji, ale tak jest! Miałem na myśli to, co naprawdę dzieje się w kompilatorach. Skompiluj go na ostatnim GCC, zobacz wynikowy asm i sprawdź, czy mi nie wierzysz!
Kos

Spróbuj dodać, std::array<int, fact(2)>a zobaczysz, że fakt () nie jest oceniany podczas kompilacji. Po prostu optymalizator GCC wykonuje dobrą robotę.

1
Tak powiedziałem ... czy naprawdę jestem tak niejasny? Zobacz ostatni akapit
Kos,

5

Właśnie zacząłem zmieniać projekt na c ++ 11 i natrafiłem na całkowicie dobrą sytuację dla constexpr, który oczyszcza alternatywne metody wykonywania tej samej operacji. Kluczową kwestią jest to, że możesz umieścić funkcję w deklaracji rozmiaru tablicy tylko wtedy, gdy jest zadeklarowana jako constexpr. Jest wiele sytuacji, w których widzę, że jest to bardzo przydatne, gdy idzie o krok naprzód z obszarem kodu, w który jestem zaangażowany.

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}

4
Można to również zapisać: const size_t MaxIPV4StringLength = sizeof ("255.255.255.255");
Superfly Jon

static inline constexpr const autoprawdopodobnie jest lepiej.
JiaHao Xu,

3

Wszystkie pozostałe odpowiedzi są świetne, chcę tylko dać fajny przykład jednej rzeczy, którą możesz zrobić z constexpr, która jest niesamowita. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) to analizator składni HTML i silnik szablonów. Oznacza to, że możesz umieścić HTML i wydostać się z drzewa, którym można manipulować. Wykonanie analizy składniowej w czasie kompilacji może zapewnić dodatkową wydajność.

Z przykładu strony github:

#include <iostream>
#include "seephit.h"
using namespace std;



int main()
{
  constexpr auto parser =
    R"*(
    <span >
    <p  color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p  >
    </span>
    )*"_html;

  spt::tree spt_tree(parser);

  spt::template_dict dct;
  dct["name"] = "Mary";
  dct["profession"] = "doctor";
  dct["city"] = "London";

  spt_tree.root.render(cerr, dct);
  cerr << endl;

  dct["city"] = "New York";
  dct["name"] = "John";
  dct["profession"] = "janitor";

  spt_tree.root.render(cerr, dct);
  cerr << endl;
}

1

Twój podstawowy przykład podaje ten sam argument, co argument samych stałych. Dlaczego warto korzystać

static const int x = 5;
int arr[x];

nad

int arr[5];

Ponieważ jest to o wiele łatwiejsze w utrzymaniu. Korzystanie z constexpr jest dużo, dużo szybsze w pisaniu i czytaniu niż istniejące techniki metaprogramowania.


0

Może włączyć nowe optymalizacje. consttradycyjnie jest wskazówką dla systemu typów i nie można jej użyć do optymalizacji (np. constfunkcja członka może const_casti modyfikuje obiekt mimo to, legalnie, więc constnie można ufać optymalizacji).

constexproznacza, że ​​wyrażenie jest naprawdę stałe, pod warunkiem, że dane wejściowe do funkcji są stałe. Rozważać:

class MyInterface {
public:
    int GetNumber() const = 0;
};

Jeśli zostanie to ujawnione w innym module, kompilator nie może ufać, że GetNumber()nie zwróci różnych wartości za każdym razem, gdy jest wywoływany - nawet kolejno bez żadnych wywołań pośrednich między nimi - ponieważ constmógł zostać odrzucony w implementacji. (Oczywiście każdy programista, który to zrobił, powinien zostać zastrzelony, ale język na to pozwala, dlatego kompilator musi przestrzegać zasad.)

Dodawanie constexpr:

class MyInterface {
public:
    constexpr int GetNumber() const = 0;
};

Kompilator może teraz zastosować optymalizację, w której zwracana wartość GetNumber()jest buforowana, i eliminować dodatkowe wywołania GetNumber(), ponieważ constexprjest to silniejsza gwarancja, że ​​wartość zwracana nie ulegnie zmianie.


Właściwie const można go wykorzystać do optymalizacji ... Nieokreślone zachowanie polega na modyfikacji stałej zdefiniowanej wartości nawet po const_castIIRC. Spodziewałbym się, że będzie spójny dla constfunkcji składowych, ale muszę to sprawdzić za pomocą standardu. Oznaczałoby to, że kompilator może bezpiecznie przeprowadzać tam optymalizacje.
Kos

1
@Warren: nie ma znaczenia, czy optymalizacja została faktycznie wykonana, jest po prostu dozwolona. @Kos: to mało znana subtelność, że jeśli oryginalny obiekt nie został zadeklarowany jako const ( int xvs. const int x), to można go bezpiecznie zmodyfikować, const_castodsuwając const na wskaźniku / odnośniku do niego. W przeciwnym razie const_castzawsze wywoływałby niezdefiniowane zachowanie i byłby bezużyteczny :) W tym przypadku kompilator nie ma informacji o trwałości oryginalnego obiektu, więc nie może powiedzieć.
AshleysBrain

@ Kos Nie sądzę, że const_cast jest jedynym problemem tutaj. Metoda const może odczytywać, a nawet modyfikować zmienną globalną. I odwrotnie, ktoś z innego wątku może również zmodyfikować obiekt const między wywołaniami.
enobayram

1
„= 0” jest tutaj niepoprawne i powinno zostać usunięte. Zrobiłbym to sam, ale nie jestem pewien, czy jest to zgodne z protokołem SO.
KnowItAllWannabe

Oba przykłady są nieprawidłowe: pierwszy ( int GetNumber() const = 0;) powinien zadeklarować GetNumber()metodę wirtualną. Druga ( constexpr int GetNumber() const = 0;) jest nieprawidłowa, ponieważ czysty specyfikator ( = 0) sugeruje, że metoda jest wirtualna, ale constexpr nie może być wirtualny (ref: en.cppreference.com/w/cpp/language/constexpr )
stj

-1

Kiedy stosować constexpr:

  1. ilekroć istnieje stała czasowa kompilacji.

Chociaż zgadzam się z tobą, ta odpowiedź nie wyjaśnia, dlaczego constexpr należy preferować makra lub preprocesory const.
Sneftel

-3

Przydaje się do czegoś takiego

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

int some_arr[MeaningOfLife()];

Połącz to z klasą cech lub podobną, a stanie się to całkiem przydatne.


4
W twoim przykładzie oferuje zero przewagi nad zwykłą stałą, więc tak naprawdę nie odpowiada na pytanie.
czerwiec

Jest to wymyślony przykład, wyobraź sobie, że MeaningOfLife () uzyskuje swoją wartość skądinąd, powiedz inną funkcję lub #define lub szereg term. Możesz nie wiedzieć, co zwraca, może to być kod biblioteki. Inne przykłady, wyobraź sobie niezmienny pojemnik, który ma metodę constexpr size (). Możesz teraz zrobić int arr [container.size ()];
plivesey

2
@plivesey, możesz edytować swoją odpowiedź, podając lepszy przykład.
Mukesh,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.