"Constexpr" ve "const" arasındaki fark


593

Arasındaki fark nedir constexprve const?

  • Bunlardan sadece birini ne zaman kullanabilirim?
  • Her ikisini de ne zaman kullanabilirim ve nasıl seçmeliyim?

71
constexprderleme zamanı sabiti oluşturur; constbasitçe, değerin değiştirilemeyeceği anlamına gelir.
0x499602D2


boost/hanaKütüphaneden bu makale constexpr, kullanabileceğiniz constexprve kullanamayacağınız bazı konuları aydınlatabilir
Andry

0x499602D2 @ " sadece bu değer değiştirilemez anlamına gelir , bir sabit başlatıldı bir sayısal için değişiklik olmayan bir değer" olan , aynı zamanda bir derleme zaman sabiti.
curiousguy

@curiousguy Evet yorumum çok basitti. Kuşkusuz ben de constexprgeri dönmek için yeni oldu :)
0x499602D2

Yanıtlar:


587

Temel anlam ve sözdizimi

Her iki anahtar kelime de işlevlerin yanı sıra nesnelerin bildiriminde de kullanılabilir. Nesnelere uygulandığında temel fark şudur:

  • constbir nesneyi sabit olarak bildirir . Bu, bir kez başlatıldığında, o nesnenin değerinin değişmeyeceğini ve derleyicinin bu gerçeği optimizasyonlar için kullanabileceğini garanti eder. Ayrıca programlayıcının, başlatma işleminden sonra değiştirilmesi amaçlanmayan nesneleri değiştiren kod yazmasını önlemeye yardımcı olur.

  • constexprbir nesneyi, Standard'ın sabit ifadeler olarak adlandırdığı şeyde kullanıma uygun olarak bildirir . Ancak constexprbunu yapmanın tek yolu olmadığını unutmayın .

Uygulandığında fonksiyonları temel fark şudur:

  • constgenel olarak işlevler için değil, yalnızca statik olmayan üye işlevleri için kullanılabilir. Üye işlevinin statik olmayan veri üyelerinden hiçbirini değiştirmediğini garanti eder.

  • constexprhem üye hem de üye olmayan işlevlerin yanı sıra yapıcılarla da kullanılabilir. İşlevin sabit ifadelerde kullanılmaya uygun olduğunu bildirir . Derleyici yalnızca işlev belirli ölçütleri (7.1.5 / 3,4), en önemlisi (†) karşılıyorsa kabul edecektir :

    • İşlev gövdesi sanal olmayan ve son derece basit olmalıdır: typedefs ve static varsayımlar dışında yalnızca tek bir returndeyime izin verilir. Bir kurucu söz konusu olduğunda, yalnızca bir başlatma listesine, typedefs'e ve static assert'e izin verilir. ( = defaultve = deletebuna da izin verilir.)
    • C ++ 14'ten itibaren kurallar daha rahattır, o zamandan beri bir constexpr işlevi içinde izin verilir: asmbildirim, bir gotodeyim, bir etikete sahip olmayan bir deyim caseve defaulttry-block, değişmez olmayan bir değişkenin tanımı, statik veya evre saklama süresi değişkeninin tanımı, başlatma yapılmayan bir değişkenin tanımı.
    • Bağımsız değişkenler ve dönüş türü değişmez türler olmalıdır (yani genel olarak konuşursak çok basit türler, tipik olarak skaler veya kümeler)

Sabit ifadeler

Yukarıda belirtildiği gibi, constexprher iki nesneyi ve sabit ifadelerde kullanıma uygun işlevleri bildirir. Sabit bir ifade yalnızca sabitten daha fazlasıdır:

  • Derleme zamanı değerlendirmesi gerektiren yerlerde, örneğin şablon parametreleri ve dizi boyutu belirteçleri için kullanılabilir:

    template<int N>
    class fixed_size_list
    { /*...*/ };
    
    fixed_size_list<X> mylist;  // X must be an integer constant expression
    
    int numbers[X];  // X must be an integer constant expression
  • Ancak dikkat:

    • Bir şeyi constexprderlemek, derleme zamanında değerlendirileceğini garanti etmez. Bunun için kullanılabilir , ancak çalışma zamanında değerlendirilen diğer yerlerde de kullanılabilir.

    • Bir nesne olabilir sürekli ifadelerde kullanıma uygun olması olmadan ilan edilen constexpr. Misal:

      int main()
      {
        const int N = 3;
        int numbers[N] = {1, 2, 3};  // N is constant expression
      }

    Bu mümkündür, çünkü Nsabit ve bildirim zamanında bir değişmezle başlatılırken, bildirilmese bile, sabit bir ifade ölçütlerini karşılar constexpr.

Peki ne zaman kullanmak zorundayım constexpr?

  • Bir nesne gibi N, yukarıda sabit ifadesi olarak kullanılabilir olmadan beyan edilen constexpr. Bu, tüm nesneler için geçerlidir:

    • const
    • integral veya sayım tipi ve
    • sabit bir ifade olan bir ifade ile bildirim zamanında başlatıldı

    [Bu, §5.19 / 2'den kaynaklanmaktadır: Sabit bir ifade, […] bir integral veya numaralandırma türü […] “değerinin değişmesi nedeniyle bir lvalue-rvalue modifikasyonu içeren bir alt ifade içermemelidir. daha önce bunun tüm değişmez türler için geçerli olduğunu iddia ediyordu.]

  • Bir işlevin sabit ifadelerde kullanılmaya uygun olması için, açıkça belirtilmesi gerekirconstexpr ; sadece sabit ekspresyon fonksiyonları için kriterleri yerine getirmesi yeterli değildir. Misal:

    template<int N>
    class list
    { };
    
    constexpr int sqr1(int arg)
    { return arg * arg; }
    
    int sqr2(int arg)
    { return arg * arg; }
    
    int main()
    {
      const int X = 2;
      list<sqr1(X)> mylist1;  // OK: sqr1 is constexpr
      list<sqr2(X)> mylist2;  // wrong: sqr2 is not constexpr
    }

İkisini birlikteconst ve birlikte ne zaman kullanabilirim constexpr ?

A. Nesne bildirimlerinde. Her iki anahtar kelime de bildirilecek aynı nesneyi ifade ettiğinde bu hiçbir zaman gerekli değildir. constexprima eder const.

constexpr const int N = 5;

aynıdır

constexpr int N = 5;

Ancak, anahtar kelimelerin her birinin bildirimin farklı bölümlerine atıfta bulunduğu durumlar olabileceğini unutmayın:

static constexpr int N = 3;

int main()
{
  constexpr const int *NP = &N;
}

Burada NPbir adres sabit ifadesi, yani sabit bir ifade olan bir işaretçi olarak bildirilir. İşte (. Adresi statik / küresel sabit ifadesi için adres operatörü uygulanarak oluşturulursa, bu mümkündür), hem constexprve constgereklidir: constexprHer zaman ifade eder (burada beyan edilen NPsüre,) constkarşılık gelir int(bir pointer- beyan için-Kat). - constifadesinin kaldırılması ifadeyi yasadışı hale getirir (çünkü (a) sabit olmayan bir nesneye yönelik bir işaretçi sabit bir ifade olamaz ve (b) &Naslında bir işaretçi-sabittir).

B. Üye işlev bildirimlerinde. C ++ 11, constexpranlaşılacağı constC ++ 14 ve C ++ 17 durum değildir ise. C ++ 11 altında bir üye işlevi

constexpr void f();

olarak ilan edilmesi gerekiyor

constexpr void f() const;

C ++ 14 altında hala bir constişlev olarak kullanılabilir .


3
IMO "derleme zamanında değerlendirilmemeli" onları "derleme zamanında değerlendirildi" olarak düşünmekten daha az yardımcı olur. Sabit bir ifadedeki kısıtlamalar, bir derleyicinin bunu değerlendirmesinin nispeten kolay olacağı anlamına gelir. Bir derleyici bu kısıtlamaların karşılanmaması durumunda şikayet etmelidir. Hiçbir yan etkisi olmadığından, bir derleyicinin bunu "değerlendirip değerlendirmediğini" fark edemezsiniz.
aschepler

10
@aschepler Elbette. Benim asıl nokta, constexprsabit olmayan bir ifade, örneğin sıradan bir değişken üzerinde bir işlev çağırırsanız , bu tamamen yasaldır ve işlevin diğer işlevler gibi kullanılacağıdır. Derleme zamanında değerlendirilmez (çünkü yapamaz). Belki de bunun çok açık olduğunu düşünüyorsunuz - ama ilan edilen bir fonksiyonun constexprher zaman derleme zamanında değerlendirileceğini ifade etsem, yanlış yorumlanabilir.
jogojapan

5
Evet, constexprfonksiyonlardan değil nesnelerden bahsediyordum . constexprNesneler üzerinde değerlerin derleme zamanı değerlendirmesini zorlamak ve constexprişlevlerin derleme zamanında veya çalışma zamanında uygun şekilde değerlendirilmesine izin vermek gibi işlevler üzerinde düşünmeyi seviyorum .
aschepler

2
Bir düzeltme: 'const' sadece bir değişkenin değerini değiştiremeyeceğiniz bir kısıtlamadır; değerin değişmeyeceğine dair herhangi bir söz vermez (başka bir kişi tarafından). Bir yazma özelliği değil, bir okuma özelliği.
Jared Grubb

3
Bu cümle: Üye işlevinin statik olmayan veri üyelerinden hiçbirini değiştirmediğini garanti eder. önemli bir ayrıntıyı özlüyor. Olarak işaretlenen üyeler üye işlevleri mutabletarafından da değiştirilebilir const.
şeye kadir

119

constdeğişkenler için geçerlidir ve kodunuzda değiştirilmelerini önler .

constexprderleyiciye bu ifadenin derleme zamanı sabit değeri ile sonuçlandığını söyler , böylece dizi uzunlukları, constdeğişkenlere atama vb . yerlerde kullanılabilir. Oli tarafından verilen bağın birçok mükemmel örneği vardır.

Temel olarak bunlar 2 farklı kavramdır ve birlikte kullanılabilirler (ve kullanılmalıdırlar).



64

genel bakış

  • constbir programın bir nesnenin değerini değiştirmediğini garanti eder . Ancak, constnesnenin hangi başlatma türünden geçtiğini garanti etmez.

    Düşünmek:

    const int mx = numeric_limits<int>::max();  // OK: runtime initialization

    İşlev max()yalnızca değişmez bir değer döndürür. Ancak, başlatıcı bir işlev çağrısı olduğundan, mxçalışma zamanı başlatmaya tabi tutulur. Bu nedenle, bunu sabit bir ifade olarak kullanamazsınız :

    int arr[mx];  // error: “constant expression required”
  • constexprsize makrolar ve sabit kodlu değişmez değerler oluşturma gereksinimini karşılayan yeni bir C ++ 11 anahtar kelimesidir. Ayrıca, belirli koşullar altında, nesnelerin statik olarak başlatılmasını garanti eder . Bir ifadenin değerlendirme süresini kontrol eder. Uygulayarak kendi ifadesinin derleme zamanı değerlendirme , constexprtrue tanımlamak sağlar sabit ifadeleri derleme zamanı sabitleri dayanıyor herhangi bir kodda, konuşma genellikle zaman açısından kritik uygulamalar, sistem programlama, şablonlar için çok önemlidir ve.

Sabit ifade fonksiyonları

Bir sabit ekspresyon fonksiyonu bildirilen bir fonksiyonudur constexpr. Gövdesi sanal olmayan olmalı ve typedefs ve statik ekler dışında yalnızca tek bir dönüş ifadesinden oluşmalıdır. Bağımsız değişkenleri ve dönüş değeri değişmez türlere sahip olmalıdır. Sabit olmayan ifade bağımsız değişkenleriyle kullanılabilir, ancak bu yapıldığında sonuç sabit bir ifade değildir.

Sabit ifade işlevi, performanstan veya tür güvenliğinden ödün vermeden makroların ve sabit kodlu değişmez değerlerin yerini almak içindir .

constexpr int max() { return INT_MAX; }           // OK
constexpr long long_max() { return 2147483647; }  // OK
constexpr bool get_val()
{
    bool res = false;
    return res;
}  // error: body is not just a return statement

constexpr int square(int x)
{ return x * x; }  // OK: compile-time evaluation only if x is a constant expression
const int res = square(5);  // OK: compile-time evaluation of square(5)
int y = getval();
int n = square(y);          // OK: runtime evaluation of square(y)

Sabit ifade nesneleri

Bir sabit ifade nesne bildirilen bir amacıdır constexpr. Sabit bir ifade veya sabit-ifade bağımsız değişkenleri olan bir sabit-ifade yapıcısı tarafından oluşturulan bir değerleme ile başlatılmalıdır.

Bir sabit ifade nesnesi const, kullanılmadan önce başlatılmasını gerektirmesi ve başlatılmasının sabit bir ifade olması dışında , bildirilmiş gibi davranır . Sonuç olarak, bir sabit ifade nesnesi her zaman başka bir sabit ifadenin parçası olarak kullanılabilir.

struct S
{
    constexpr int two();      // constant-expression function
private:
    static constexpr int sz;  // constant-expression object
};
constexpr int S::sz = 256;
enum DataPacket
{
    Small = S::two(),  // error: S::two() called before it was defined
    Big = 1024
};
constexpr int S::two() { return sz*2; }
constexpr S s;
int arr[s.two()];  // OK: s.two() called after its definition

Sabit ifade kurucuları

Bir sabit ekspresyon yapıcı bildirilen bir yapıcı constexpr. Üye başlatma listesine sahip olabilir, ancak gövdesi typedefs ve statik ekler dışında boş olmalıdır. Argümanları değişmez türlere sahip olmalıdır.

Sabit ifade yapıcısı, kurucunun bağımsız değişkenlerinin tümü sabit ifadeler olması koşuluyla derleyicinin nesneyi derleme zamanında başlatmasına izin verir.

struct complex
{
    // constant-expression constructor
    constexpr complex(double r, double i) : re(r), im(i) { }  // OK: empty body
    // constant-expression functions
    constexpr double real() { return re; }
    constexpr double imag() { return im; }
private:
    double re;
    double im;
};
constexpr complex COMP(0.0, 1.0);         // creates a literal complex
double x = 1.0;
constexpr complex cx1(x, 0);              // error: x is not a constant expression
const complex cx2(x, 1);                  // OK: runtime initialization
constexpr double xx = COMP.real();        // OK: compile-time initialization
constexpr double imaglval = COMP.imag();  // OK: compile-time initialization
complex cx3(2, 4.6);                      // OK: runtime initialization

Scott Meyers'in Etkili Modern C ++ kitabından ipuçları constexpr:

  • constexpr nesneler sabittir ve derleme sırasında bilinen değerlerle başlatılır;
  • constexpr fonksiyonlar değerleri derleme sırasında bilinen argümanlarla çağrıldığında derleme zamanı sonuçları üretir;
  • constexprnesneler ve işlevler, nesne ve işlevlerden daha geniş bir bağlam aralığında kullanılabilir constexpr;
  • constexpr bir nesnenin veya işlevin arayüzünün bir parçasıdır.

Kaynak: C ++ 'da Güvenlik, Performans ve Kapsüllemeyi Geliştirmek için constexpr kullanma .


Farklı durumları gösteren harika örnek kod için teşekkürler. Diğer bazı açıklamalar kadar büyük, ben eylem kodu görmek çok daha yararlı ve anlaşılır buldum. Neler olup bittiğini anlamamı sağlamlaştırdı.
RTHarston

35

Bjarne Stroustrup'un "C ++ Programlama Dili 4. Editon" kitabına göre
const : kabaca anlamı '' Bu değeri değiştirmeyeceğime söz veriyorum '' (§7.5). Bu öncelikle arayüzleri belirtmek için kullanılır, böylece veriler değiştirilmekten korkmadan işlevlere aktarılabilir.
Derleyici, const tarafından verilen vaadi uygular.
constexpr : kabaca '' derleme zamanında değerlendirilecek '' anlamına gelir (§10.4). Bu öncelikle sabitleri belirtmek ve izin vermek
için kullanılır. Örneğin:

const int dmv = 17; // dmv is a named constant
int var = 17; // var is not a constant
constexpr double max1 = 1.4*square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4square(var); // error : var is not a constant expression
const double max3 = 1.4square(var); //OK, may be evaluated at run time
double sum(const vector<double>&); // sum will not modify its argument (§2.2.5)
vector<double> v {1.2, 3.4, 4.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression

Bir fonksiyonun sabit bir ifadede, yani derleyici tarafından değerlendirilecek bir ifadede kullanılabilmesi için constexpr olarak tanımlanması gerekir .
Örneğin:

constexpr double square(double x) { return xx; }


Özetlemek gerekirse, bir fonksiyon oldukça basit olmalıdır: sadece bir değeri hesaplayan bir return-ifadesi. Bir constexpr işlevi sabit olmayan argümanlar için kullanılabilir, ancak bu yapıldığında sonuç sabit bir ifade değildir. Sabit ifadeler gerektirmeyen bağlamlarda sabit olmayan ifade bağımsız değişkenleriyle bir constexpr işlevinin çağrılmasına izin veririz, böylece esas olarak aynı işlevi iki kez tanımlamak zorunda kalmayız: bir kez sabit ifadeler için ve bir kez değişkenler için.
Birkaç yerde, dil kuralları (örneğin dizi sınırları (§2.2.5, §7.3), vaka etiketleri (§2.2.4, §9.4.2), bazı şablon bağımsız değişkenleri (§25.2) ve constexpr kullanılarak bildirilen sabitler). Diğer durumlarda, derleme zamanı değerlendirmesi performans için önemlidir. Performans sorunlarından bağımsız olarak, değişmezlik kavramı (değişmeyen bir duruma sahip bir nesnenin) önemli bir tasarım konusudur (§10.4).


hala performans sorunları var. Çalışma zamanında değerlendirilirse constexpr işlevinin, işlevin constexpr olmayan sürümünden daha yavaş olabileceği anlaşılıyor. Ayrıca sabit bir değere sahipsek "const" veya "constexpr" i mi tercih etmeliyiz? (daha fazla stil sorusu oluşturulan derleme aynı görünüyor)
CoffeDeveloper

31

Hem constve constexprdeğişkenler ve fonksiyonlar uygulanabilir. Birbirlerine benzemelerine rağmen, aslında çok farklı kavramlardır.

Hem constve hem constexprbaşlangıç ​​değerlerinden sonra değerlerinin değiştirilemeyeceği anlamına gelir. Yani mesela:

const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

Arasındaki temel fark, constve constexprbunların başlatma değerleri bilinen zamanı (değerlendirildi). constDeğişkenlerin değerleri hem derleme zamanında hem de çalışma zamanında constexprdeğerlendirilebilirken , her zaman derleme zamanında değerlendirilir. Örneğin:

int temp=rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

Değerin derleme zamanında veya çalışma zamanında bilinip bilinmediğini bilmenin en önemli avantajı, derleme süresi sabitlerinin her zaman derleme sabitine ihtiyaç duyulduğunda kullanılabilmesidir. Örneğin, C ++ değişken uzunluklu C dizilerini belirtmenize izin vermez.

int temp=rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

Yani şu anlama gelir:

const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

Yani constdeğişkenler tanımlayabilirsiniz hem derleme zamanı sabitleri gibi size1o dizi boyutları ve belirtmek için kullanılabilir çalışma zamanı sabitleri gibi size2sadece çalışma zamanında bilinir ve dizi boyutlarını tanımlamak için kullanılamaz. Öte yandan constexprher zaman dizi boyutlarını belirleyebilen derleme zamanı sabitlerini tanımlayın.

Her ikisi de constve constexprişlevlere de uygulanabilir. Bir constişlev, constanahtar sözcük uygulamasının , yöntemin üye (statik olmayan) alanlarının değerlerini değiştiremeyeceği anlamına geldiği bir üye işlevi (yöntem, operatör) olmalıdır . Örneğin.

class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

A constexprfarklı bir kavramdır. Bir işlevi (üye veya üye olmayan), derleme zamanı sabitleri bağımsız değişkenleri olarak iletilirse derleme zamanında değerlendirilebilen işlev olarak işaretler . Örneğin bunu yazabilirsiniz.

constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.

Bu arada constexprişlevler, sabit olmayan argümanlar iletilse bile çağrılabilen normal C ++ işlevleridir. Ancak bu durumda sınırsız değerleri elde edersiniz.

int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.

constexprAyrıca üye işlevlerini (yöntem), operatörler ve hatta kurucular uygulanabilir. Örneğin.

class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

Daha 'çılgın' bir örnek.

class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.

Ayrıca, C, constexpr intvar ama yazıldığındanconst int
curiousguy

8

@ 0x499602d2'nin daha önce işaret ettiği gibi, constyalnızca constexpr(C ++ 11'de tanıtılan) değişkenin bir derleme zaman sabiti olduğunu garanti ettiği gibi bir değerin değiştirilmesinden sonra değiştirilememesini sağlar .
Aşağıdaki örneği düşünün (LearnCpp.com'dan):

cout << "Enter your age: ";
int age;
cin >> age;

const int myAge{age};        // works
constexpr int someAge{age};  // error: age can only be resolved at runtime

5

A const int var, çalışma zamanında dinamik olarak bir değere ayarlanabilir ve bu değere ayarlandıktan sonra artık değiştirilemez.

A constexpr int varçalışma zamanında dinamik olarak ayarlanamaz, daha çok derleme zamanında ayarlanabilir. Ve bu değere ayarlandığında, artık değiştirilemez.

İşte sağlam bir örnek:

int main(int argc, char*argv[]) {
    const int p = argc; 
    // p = 69; // cannot change p because it is a const
    // constexpr int q = argc; // cannot be, bcoz argc cannot be computed at compile time 
    constexpr int r = 2^3; // this works!
    // r = 42; // same as const too, it cannot be changed
}

Yukarıdaki snippet iyi derler ve hataya neden olanları yorumladım.

Buradaki anahtar kavramlar, dikkat çekmek için bir kavramdır compile timeve run time. Çalışma ** know **zamanında performansı iyileştirmek için derleme zamanında belirli şeyleri mümkün olduğunca artırmak amacıyla C ++ 'a yeni yenilikler getirildi .


3

Cevapların hiçbirinin tam olarak hangi yan etkileri olduğunu veya gerçekten ne olduğunu netleştirdiğini düşünmüyorum.

constexprve constad alanında / dosya alanında, bir değişmez değer veya ifade ile başlatıldığında özdeştir; ancak bir işlevle, constherhangi bir işlev tarafından başlatılabilir, ancak constexprbir constexpr olmayan (constexpr veya constexpr olmayan bir ifade ile işaretlenmeyen bir işlev) tarafından başlatılan bir derleyici hatası oluşturur. Her ikisi de constexprve constdeğişkenler için dolaylı olarak iç bağlantıdır (aslında, -O1 ve daha güçlü derleme durumunda bağlantı aşamasına ulaşmak için hayatta kalmazlar ve staticderleyiciyi için constveya constexprsırasında dahili (yerel) bir bağlayıcı sembolü yaymaya zorlamazlar . -O1 veya daha güçlü; değişkenin adresini alırsanız bunu yapar constve ie constexprile ifade edilmedikçe dahili bir sembol olacaktır .externextern constexpr/const int i = 3;kullanılması gerekir). Bir fonksiyonu olarak, constexprişlev kalıcı (bakılmaksızın bağlayan sahne ulaşmak asla yapar externya inline, tanım ya O0 veya -Ofast olarak) oysa constyapmaz ve staticve inlinesadece -O1 ve üzeri üzerinde bu etkisi vardır. Bir zaman const/ constexprdeğişkeni tarafından başlatıldığını constexprfonksiyonu, yük her zaman herhangi bir optimizasyon bayrağıyla dışarı optimize edilmiştir, ancak işlevi yalnızca eğer dışarı optimize asla staticveya inlinedeğişken bir değilse veya eğer const/ constexpr.

Standart derleme (-O0)

#include<iostream>
constexpr int multiply (int x, int y)
{

  return x * y;
}

extern const int val = multiply(10,10);
int main () {
  std::cout << val;
} 

derlemek

val:
        .long   100  //extra external definition supplied due to extern

main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 100 //substituted in as an immediate
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 

ancak

#include<iostream>
const int multiply (int x, int y)
{

  return x * y;
}

const int val = multiply(10,10); //constexpr is an error
int main () {
  std::cout << val;
}

Derleme

multiply(int, int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-8]
        pop     rbp
        ret

main:
        push    rbp
        mov     rbp, rsp
        mov     eax, DWORD PTR val[rip]
        mov     esi, eax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     eax, 0
        pop     rbp
        ret

__static_initialization_and_destruction_0(int, int):
        . 
        . 
        . 
        mov     esi, 10
        mov     edi, 10
        call    multiply(int, int)
        mov     DWORD PTR val[rip], eax

Bu açıkça, dosya kapsamı değişkeninin constexprbaşlatılmasının const/constexprderleme zamanında gerçekleşmesine ve hiçbir global sembol üretmemesine neden olurken, onu kullanmamak mainçalışma zamanında daha önce başlatılmaya neden olur .

-Ofast kullanarak derleme

-Ofast bile yükü optimize etmez! https://godbolt.org/z/r-mhif , yani ihtiyacınız var constexpr


constexprfonksiyonlar constexpraynı sonuç için diğer fonksiyonların içinden de çağrılabilir . constexprbir işlev üzerinde ayrıca derleme zamanında yapılamayan hiçbir şeyin kullanılmasını engeller; örneğin, <<operatörün çağrılması std::cout.

constexprblok kapsamı, constexpr olmayan bir işlev tarafından başlatıldığında bir hata oluşturmasıyla aynı şekilde davranır; değer de anında değiştirilir.

Sonunda, asıl amacı C'nin satır içi işlevi gibidir, ancak işlev yalnızca dosya kapsamı değişkenlerini başlatmak için kullanıldığında etkilidir (işlevler C üzerinde yapamaz, ancak C ++ üzerinde yapabilir, çünkü dosyanın dinamik olarak başlatılmasına izin verir. kapsam değişkenleri), ancak işlev , C extern/staticile yapabileceğinizi bile olsa, bağlayıcıya genel / yerel bir sembolü de dışa aktaramaz inline; blok kapsamı değişken atama işlevleri yalnızca constexprC ve C ++ olmadan -O1 optimizasyonu kullanılarak satır içine alınabilir .


Bağlayıcı güzel nokta. Daha az sembol sızıntısına neden olduğu için constexpr kullanmak genel olarak daha güvenli kabul edilebilir mi?
Neil McGill

1
@NeilMcGill gerçekten satır içi ve statik değil çünkü derleyicinin -O1 veya daha güçlü kullanarak derleme için çarpma için yerel bir sembol yaymasına neden olmaz. Constexpr, val için yükü optimize eden tek şeydir, ancak işlevden önce statik veya satır içi koymakla aynıdır. Ben de başka bir şey unuttum. Constexpr, -O0, statik ve satır içi işlevler için bir sembol yaymayan tek anahtar kelimedir
Lewis Kelsey

1

Her şeyden önce, her ikisi de c ++ 'da niteleyicilerdir. Bildirilen bir değişkenin başlatılması gerekir ve gelecekte değiştirilemez. Bu nedenle genellikle sabit olarak bildirilen bir değişken derlenmeden önce bile bir değere sahip olacaktır.

Ancak, constexpr için biraz farklıdır.

Constexpr için, programın derlenmesi sırasında değerlendirilebilecek bir ifade verebilirsiniz.

Açıkçası, sınırlayıcı olarak bildirilen değişken, tıpkı sabit gibi, gelecekte değiştirilemez.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.