Derleme zamanında bir C dizesinin hesaplama uzunluğu. Bu gerçekten bir konstexpr mi?


95

Derleme zamanında bir dizgenin uzunluğunu hesaplamaya çalışıyorum. Bunu yapmak için aşağıdaki kodu kullanıyorum:

#include <cstdio>

int constexpr length(const char* str)
{
    return *str ? 1 + length(str + 1) : 0;
}

int main()
{
    printf("%d %d", length("abcd"), length("abcdefgh"));
}

Her şey beklendiği gibi çalışır, program 4 ve 8'i yazdırır. Clang tarafından oluşturulan derleme kodu sonuçların derleme zamanında hesaplandığını gösterir:

0x100000f5e:  leaq   0x35(%rip), %rdi          ; "%d %d"
0x100000f65:  movl   $0x4, %esi
0x100000f6a:  movl   $0x8, %edx
0x100000f6f:  xorl   %eax, %eax
0x100000f71:  callq  0x100000f7a               ; symbol stub for: printf

Sorum: lengthfonksiyonun derleme zamanı değerlendirileceği standart tarafından garanti ediliyor mu?

Bu doğruysa, derleme zamanı dizesi değişmezleri hesaplamaları için kapı az önce açıldı ... örneğin, derleme zamanında hash'leri hesaplayabilirim ve daha pek çok şey ...


3
Parametre sabit bir ifade olduğu sürece, olması gerekir.
chris

1
@chris Sabit bir ifade olabilen bir şeyin, sabit bir ifade gerektirmeyen bir bağlamda kullanıldığında derleme zamanında değerlendirilmesi gerektiğine dair bir garanti var mı ?
TC

12
BTW, dahil <cstdio>ve sonra arama ::printftaşınabilir değildir. Standart sadece <cstdio>sağlamayı gerektirir std::printf.
Ben Voigt

1
@BenVoigt Tamam, bunu belirttiğiniz için teşekkürler :) Başlangıçta std :: cout kullandım, ancak üretilen kod gerçek değerleri bulmak için oldukça büyüktü :)
Mircea Ispas

3
@Felics Optimizasyon ve kullanımla ilgili soruları yanıtlarken sık sık godbolt kullanıyorum printf, ilgilenilmesi gereken çok daha az koda yol açabilir.
Shafik Yaghmour

Yanıtlar:


76

Sabit ifadelerin derleme sırasında değerlendirileceği garanti edilmez, yalnızca taslak C ++ standart bölümünden normatif olmayan bir alıntıya sahibiz Yine de şunu söyleyen 5.19 Sabit ifadeler :

[...]> [Not: Sabit ifadeler çeviri sırasında değerlendirilebilir. - son not]

constexprDerleme zamanında değerlendirildiğinden emin olmak için sonucu değişkene atayabilirsiniz, bunu Bjarne Stroustrup'un ( vurgu benim ) yazan C ++ 11 referansından görebiliriz :

İfadeleri derleme zamanında değerlendirebilmenin yanı sıra, ifadelerin derleme zamanında değerlendirilmesini isteyebilmek istiyoruz ; Bir değişken tanımının önünde constexpr bunu yapar (ve const anlamına gelir):

Örneğin:

constexpr int len1 = length("abcd") ;

Bjarne Stroustrup, bu isocpp blog girişinde derleme zamanı değerlendirmesini ne zaman sağlayabileceğimizin bir özetini veriyor ve şöyle diyor:

[...] Doğru cevap - Herb tarafından belirtildiği gibi - standarda göre bir constexpr fonksiyonunun sabit bir ifade olarak kullanılmadığı sürece derleyici zamanında veya çalışma zamanında değerlendirilebileceğidir, bu durumda derleme sırasında değerlendirilmesi gerekir -zaman. Derleme zamanı değerlendirmesini garanti etmek için, onu sabit bir ifadenin gerekli olduğu yerlerde (örneğin, bir dizi bağlı veya bir durum etiketi olarak) kullanmalı veya bir constexpr'i başlatmak için kullanmalıyız. Kendine saygı duyan hiçbir derleyicinin, başlangıçta söylediğim şeyi yapmak için optimizasyon fırsatını kaçırmayacağını umuyorum: "Bir constexpr işlevi, tüm argümanları sabit ifadelerse, derleme zamanında değerlendirilir."

Dolayısıyla bu, derleme zamanında değerlendirilmesi gereken iki durumu özetlemektedir:

  1. Bunu sabit bir ifadenin gerekli olduğu yerlerde kullanın , bu, bir dizi bağlama gibi tümcecik shall be ... converted constant expressionveya shall be ... constant expressionkullanılan draft standardında herhangi bir yer gibi görünür.
  2. constexprYukarıda özetlediğim gibi a'yı başlatmak için kullanın .

4
Bununla birlikte, prensipte bir derleyicinin dahili veya hiçbir bağlantısı olmayan bir nesneyi görme yetkisi vardır constexpr int x = 5;, derleme zamanında değeri gerektirmediğini gözlemleyin (bir şablon parametresi veya başka bir şey olarak kullanılmadığını varsayarak) ve gerçekte yayar 1 ve 4 ekleme işleminin 5 anlık değerini kullanarak çalışma zamanında başlangıç ​​değerini hesaplayan kod. Daha gerçekçi bir örnek: Derleyici bir özyineleme sınırına ulaşabilir ve hesaplamayı çalışma zamanına erteleyebilir. Derleyiciyi değeri gerçekten kullanmaya zorlayan bir şey yapmadığınız sürece, "derleme zamanında değerlendirilmesi garantili" bir QOI sorunudur.
Steve Jessop

@SteveJessop Bjarne , çeviride değerlendirilen sabit bir ifade aracı olarak kullanılan taslak standartta bulabildiğim bir analoğu olmayan bir kavramı kullanıyor gibi görünüyor . Öyleyse, standardın söylediklerini açıkça belirtmediği anlaşılıyor, bu yüzden sizinle aynı fikirde olma eğilimindeyim. Hem Bjarne hem de Herb bu konuda hemfikir gibi görünse de, bu sadece belirsiz olduğunu gösterebilir.
Shafik Yaghmour

2
Sanırım her ikisi de, standartlara uyan ancak kasten engelleyici olan derleyicinin aksine, sadece "kendine saygı duyan derleyiciler" düşünüyorlar. Standardın gerçekte neyi garanti ettiğine dair bir akıl yürütme aracı olarak kullanışlıdır , başka bir şey değil ;-)
Steve Jessop

3
@SteveJessop Kötü şöhretli (ve ne yazık ki var olmayan) Hell ++ gibi kasıtlı olarak engelleyici derleyiciler. Böyle bir şey, uygunluğu / taşınabilirliği test etmek için gerçekten harika olurdu.
Angew artık

Sanki kuralı uyarınca, değeri görünen bir derleme süresi sabiti olarak kullanmak bile yeterli değildir: derleyici, kaynağınızın bir kopyasını göndermekte ve onu çalışma zamanında yeniden derlemekte veya bir tür değişken, ya da constexprhesaplamanızı tamamen kötülükten anlamsız bir şekilde yeniden çalıştırın . Hatta belirli bir kaynak satırında karakter başına 1 saniye beklemek veya belirli bir kaynak satırını alıp bir satranç pozisyonu başlatmak için kullanmak ve ardından kimin kazandığını belirlemek için her iki tarafı da oynamak ücretsizdir.
Yakk - Adam Nevraumont

27

Bir constexprişleve yapılan bir çağrının temel bir sabit ifadeyle mi sonuçlandığını yoksa yalnızca optimize edildiğini bulmak gerçekten çok kolay :

Sabit bir ifadenin gerekli olduğu bir bağlamda kullanın.

int main()
{
    constexpr int test_const = length("abcd");
    std::array<char,length("abcdefgh")> test_const2;
}

4
... ve -pedanticgcc kullanıyorsanız, ile derleyin . Aksi takdirde, hiçbir uyarı ve hata
almazsınız

@ BЈовић Veya GCC'nin bir şablon argümanı gibi potansiyel olarak araya giren uzantıların olmadığı bir bağlamda kullanın.
Angew artık

Gitmeyeceklerini \ t Bir enum kesmek daha güvenilir? Gibi enum { Whatever = length("str") }?
sharptooth

18
Söylemeye değerstatic_assert(length("str") == 3, "");
Chris

8
constexpr auto test = /*...*/;muhtemelen en genel ve anlaşılır olanıdır.
TC

20

Sadece bir not, modern derleyicilerin (gcc-4.x gibi) strlenderleme zamanında dizgi değişmezleri için yaptıkları , çünkü normalde içsel bir işlev olarak tanımlanır . Hiçbir optimizasyon etkinleştirilmeden. Sonuç bir derleme zamanı sabiti olmamasına rağmen.

Örneğin:

printf("%zu\n", strlen("abc"));

Sonuçlar:

movl    $3, %esi    # strlen("abc")
movl    $.LC0, %edi # "%zu\n"
movl    $0, %eax
call    printf

Unutmayın, bu strlenyerleşik bir işlev olduğu için işe yarıyor , eğer kullanırsak -fno-builtinsonu çalışma zamanında çağırmaya geri dönüyor, canlı görün
Shafik Yaghmour

strlenbenim constexpriçin bile -fno-nonansi-builtins( -fno-builtinsg ++ 'da artık yok gibi görünüyor ). "Constexpr" diyorum çünkü bunu yapabilirim template<int> void foo();ve foo<strlen("hi")>(); g ++ - 4.8.4
Aaron McDaid

19

Tekrarlamalı olmadan derleme zamanında bir dizgenin uzunluğunu hesaplayan başka bir işlev önermeme izin verin.

template< size_t N >
constexpr size_t length( char const (&)[N] )
{
  return N-1;
}

İdeone'da bu örnek koda bir göz atın .


5
Gömülü '\ 0' nedeniyle strlen'e eşit olamaz: strlen ("hi \ 0there")! = Uzunluk ("hi \ 0there")
unkulunkulu

Bu doğru yoldur, bu Etkili Modern C ++ 'daki bir örnektir (eğer doğru hatırlıyorsam). Bununla birlikte, tamamen constexpr olan güzel bir dizge sınıfı var, şu yanıta bakın: Scott Schurr's str_const , belki bu daha kullanışlı (ve daha az C stili) olacaktır.
QuantumKarl

@MikeWeir Ops, bu garip. İşte çeşitli bağlantılar vardır: soruya bağlantı , kağıda bağlantı , Git üzerinde kaynağına bağlantı
QuantumKarl

şimdi yapın: char temp[256]; sprintf(temp, "%u", 2); if(1 != length(temp)) printf("Your solution doesn't work"); ideone.com/IfKUHV
Pablo Ariel

7

constexprHerhangi bir makul derleyici bunu uygun optimizasyon düzeyleri etkinleştirildiğinde gerçekleştirecek olsa da , bir işlevin derleme zamanında değerlendirileceğinin garantisi yoktur . Öte yandan, şablon parametreleri gerekir derleme zamanında değerlendirilmelidir.

Derleme zamanında değerlendirmeyi zorlamak için aşağıdaki numarayı kullandım. Maalesef yalnızca integral değerlerle çalışır (yani kayan nokta değerleriyle değil).

template<typename T, T V>
struct static_eval
{
  static constexpr T value = V;
};

Şimdi yazarsan

if (static_eval<int, length("hello, world")>::value > 7) { ... }

ifdeyimin çalışma zamanı ek yükü olmayan bir derleme zamanı sabiti olduğundan emin olabilirsiniz .


8
veya sadece std :: integral_constant <int, length (...)> :: value
Mircea Ispas

1
Örnek yana anlamsız kullanım biraz lenolmak constexpraracı lengthyine derleme zamanında değerlendirilmelidir.
chris

@chris Ben bilmiyordum gerekir ben o gözlemledim konacak olan benim derleyici ile.
5gon12eder

Tamam, diğer cevapların çoğuna göre yapması gereken, bu yüzden örneği daha az anlamsız olacak şekilde değiştirdim. Aslında, ifilk olarak hile kullandığım bir koşuldu (derleyicinin ölü kodu ortadan kaldırması gerekliydi).
5gon12eder

1

Wikipedia'nın Genelleştirilmiş sabit ifadeler hakkındaki girişinden kısa bir açıklama :

Bir işlev üzerinde constexpr kullanımı, bu işlevin yapabileceklerine bazı sınırlamalar getirir. İlk olarak, işlevin geçersiz olmayan bir dönüş türüne sahip olması gerekir. İkinci olarak, işlev gövdesi değişkenleri bildiremez veya yeni türler tanımlayamaz. Üçüncüsü, gövde yalnızca bildirimler, boş ifadeler ve tek bir dönüş ifadesi içerebilir. Bağımsız değişken değiştirmeden sonra, dönüş ifadesindeki ifadenin sabit bir ifade oluşturması için bağımsız değişken değerleri olmalıdır.

constexprAnahtar kelimenin bir işlev tanımından önce olması, derleyiciye bu sınırlamaların karşılanıp karşılanmadığını kontrol etmesini sağlar. Evetse ve işlev bir sabitle çağrılırsa, döndürülen değerin sabit olması garanti edilir ve bu nedenle sabit bir ifadenin gerekli olduğu her yerde kullanılabilir.


Bu koşullar , döndürülen değerin sabit olduğunu garanti etmez . Örneğin, işlev diğer bağımsız değişken değerleriyle çağrılabilir.
Ben Voigt

Doğru, @BenVoigt. Sabit bir ifade ile çağrılması için düzenledim.
kaedinger
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.