const vs değişkenler üzerinde constexpr


303

Aşağıdaki tanımlar arasında bir fark var mı?

const     double PI = 3.141592653589793;
constexpr double PI = 3.141592653589793;

Değilse, C ++ 11'de hangi stil tercih edilir?



Her ikisi de derleme zamanı sabitidir. Ancak ilkinin const_cast'ini yapabilir ve ona yazabilirsiniz. Ancak bu derleyici tarafından optimize edilecek çünkü bu derleme zamanında gerçekleştikçe "okumaları" etkilemez.
Bonita Montero

Yanıtlar:


347

Bir fark olduğuna inanıyorum. Onları daha kolay konuşabilmemiz için onları yeniden adlandıralım:

const     double PI1 = 3.141592653589793;
constexpr double PI2 = 3.141592653589793;

Hem PI1ve PI2bunları değiştiremez, yani sabittir. Ancak sadece PI2 derleme zamanı sabiti. Bu edecektir derleme sırasında başlatılır. PI1derleme zamanında veya çalışma zamanında başlatılabilir. Ayrıca, yalnızca PI2 derleme zamanı sabiti gerektiren bir bağlamda kullanılabilir. Örneğin:

constexpr double PI3 = PI1;  // error

fakat:

constexpr double PI3 = PI2;  // ok

ve:

static_assert(PI1 == 3.141592653589793, "");  // error

fakat:

static_assert(PI2 == 3.141592653589793, "");  // ok

Hangisini kullanmalısınız? İhtiyaçlarınızı karşılayanı kullanın. Derleme zamanı sabitinin gerekli olduğu bağlamlarda kullanılabilen bir derleme zaman sabitine sahip olduğunuzdan emin olmak istiyor musunuz? Çalışma zamanında yapılan bir hesaplama ile ilklendirmek ister misiniz? Vb.


60
Emin misiniz? Çünkü const int N = 10; char a[N];works ve dizi sınırları derleme zamanı sabitleri olmalıdır.
fredoverflow

10
Ben yazdım örnekleri gitmek kadar eminim (her göndermeden önce test). Ancak derleyicim, PI1bir dizide kullanmak için derleme zamanı integral sabitine dönüştürmeme izin veriyor, ancak tür olmayan bir integral şablon parametresi olarak kullanım için değil. Yani derleme-zaman PI1ayrılmaz bir tür dönüştürülebilir bana biraz hit & miss gibi görünüyor.
Howard Hinnant

34
@FredOverflow: Const olmayan dizi indeksleri yaklaşık on yıldır "çalıştı" (örneğin bunun için bir g ++ uzantısı var), ancak bu kesinlikle yasal C ++ olduğu anlamına gelmiyor (bazı daha yeni C veya C ++ standardı yasal hale getirdi , ben hangisini unuttum). Derleme zamanı sabitlerindeki farklılıklara gelince, şablon parametreleri ve enumbaşlatıcı olarak kullanım, constve arasındaki önemli iki farktır constexpr(ve her ikisi de işe yaramaz double).
Damon

17
5.19 Sabit ifadelerin 4. paragrafı [expr.const] aynı zamanda (normatif olmayan) bir notun, bir uygulamanın derleme zamanında çalışma süresinden farklı olarak (örn. Doğruluk açısından) kayan nokta aritmetiği yapmasına izin verildiğini ana hatlarıyla belirtmektedir. Yani 1 / PI1ve 1 / PI2farklı sonuçlar verebilir. Ancak bu teknikliğin bu cevaptaki tavsiye kadar önemli olduğunu düşünmüyorum.
Luc Danton

4
Ama constexpr double PI3 = PI1;benim için doğru çalışıyor. (MSVS2013 CTP). Neyi yanlış yapıyorum?
NuPagadi

77

Burada bir fark yok, ancak bir yapıcıya sahip bir tipiniz olduğunda önemli.

struct S {
    constexpr S(int);
};

const S s0(0);
constexpr S s1(1);

s0sabittir, ancak derleme zamanında başlatılacağına söz vermez. s1işaretlidir constexpr, bu yüzden sabittir ve S'yapıcısı da işaretlendiğinden constexprderleme zamanında başlatılır.

Çoğunlukla bu, çalışma zamanında başlatma zaman alıcı olacağı zaman önemlidir ve bu işi derleyici üzerine de itmek istersiniz, burada da zaman alıcıdır, ancak derlenmiş programın yürütme süresini yavaşlatmaz.


3
Katılıyorum: Vardığım sonuç constexpr, nesnenin derleme zamanı hesaplamasının imkansız olması durumunda teşhise yol açacağıydı. Daha az net olan , parametrenin şu şekilde değil bildirilmesi gerektiği gibi, sabit bir parametre bekleyen bir işlevin derleme zamanında yürütülüp yürütülemeyeceğidir : yani, çağırırsam derleme zamanında yürütülür mü? constconstexprconstexpr int foo(S)foo(s0)
Matthieu M.12

4
@MatthieuM: foo(s0)Derleme zamanında yürütülüp yürütülmeyeceğinden şüpheliyim , ancak asla bilemezsiniz: bir derleyicinin bu tür optimizasyonları yapmasına izin verilir. Kesinlikle, ne gcc 4.7.2 ne de clang 3.2 constexpr a = foo(s0);
derlememe

50

constexpr , derleme sırasında sabit ve bilinen bir değeri belirtir.
const yalnızca sabit olan bir değeri gösterir; derleme sırasında bilmek zorunlu değildir.

int sz;
constexpr auto arraySize1 = sz;    // error! sz's value unknown at compilation
std::array<int, sz> data1;         // error! same problem

constexpr auto arraySize2 = 10;    // fine, 10 is a compile-time constant
std::array<int, arraySize2> data2; // fine, arraySize2 is constexpr

Const'un constexpr ile aynı garantiyi sağlamadığını unutmayın, çünkü const nesnelerinin derleme sırasında bilinen değerlerle başlatılması gerekmez.

int sz;
const auto arraySize = sz;       // fine, arraySize is const copy of sz
std::array<int, arraySize> data; // error! arraySize's value unknown at compilation

Tüm constexpr nesneleri sabittir, ancak tüm const nesneleri constexpr değildir.

Derleyicilerin bir değişkenin derleme zamanı sabitleri gerektiren bağlamlarda kullanılabilecek bir değere sahip olmasını garanti etmesini istiyorsanız, ulaşılacak araç const değil constexpr'dir.


2
Açıklamanızı çok beğendim .. lütfen daha fazla yorum yapabilir misiniz? Gerçek hayat senaryolarında derleme zamanı sabitlerini kullanmamız gerekebilecek durumlar nerede?
Mayukh Sarkar

1
@MayukhSarkar Basitçe Google C ++ neden constexpr , örneğin stackoverflow.com/questions/4748083/…
underscore_d

18

Bir comptexpr sembolik sabitine derleme zamanında bilinen bir değer verilmelidir. Örneğin:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    constexpr int c2 = n+7;   // Error: we don’t know the value of c2
    // ...
}

Davalarını için nereye derleme zamanında bilinmemektedir ancak, C ++ sunuyor başlatmasından sonra sabit ikinci bir formu (a hiç değişmez bir değerle başlatılan bir “değişken” değeri const ). Örneğin:

constexpr int max = 100; 
void use(int n)
{
    constexpr int c1 = max+7; // OK: c1 is 107
    const int c2 = n+7; // OK, but don’t try to change the value of c2
    // ...
    c2 = 7; // error: c2 is a const
}

Bu tür “ sabit değişkenler” iki nedenden dolayı çok yaygındır:

  1. C ++ 98'de constexpr yoktu, bu yüzden insanlar const kullandı .
  2. Sabit ifadeler olmayan (değerleri derleme zamanında bilinmemektedir) ancak başlatma işleminden sonra değerleri değiştirmeyen “Değişkenler” liste öğesi kendi içinde çok yararlıdır.

Kaynak: "Programlama: C ++ Kullanarak İlkeler ve Uygulamalar" Stroustrup


25
Belki de cevabınızdaki metnin Stroustrup
Aky
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.