Bir fonksiyon içindeki statik constexpr değişkeni anlamlı mı?


193

Ben bir işlev içinde (mesela büyük bir dizi) bir değişken varsa, o ikisi ilan etmek bu da gayet mantıklı staticve constexpr? constexprdizinin derleme zamanında oluşturulduğunu garanti eder, bu yüzden staticişe yaramaz mı?

void f() {
    static constexpr int x [] = {
        // a few thousand elements
    };
    // do something with the array
}

staticoluşturulan kod veya semantik bakımından orada aslında ne birşey?

Yanıtlar:


231

Kısa cevap, sadece staticyararlı olmakla kalmayıp, her zaman istenecek oldukça iyi olmasıdır.

İlk olarak, unutmayın staticve constexprbirbirinden tamamen bağımsızdır. staticyürütme sırasında nesnenin ömrünü tanımlar; constexprnesnenin derleme sırasında kullanılabilir olması gerektiğini belirtir. Derleme ve yürütme hem zaman hem de mekânda ayrık ve bitişik değildir. Dolayısıyla program derlendikten sonra constexprartık geçerli değildir.

İlan Her değişken constexprörtülü olduğunu constancak constve staticneredeyse diktir (etkileşim hariç static consttamsayılar.)

C++Nesne modeli (§1.9) tüm bit-alanları bellek en az bir bayt işgal ve adresleri dışında nesneleri gerektirir; ayrıca, belirli bir anda bir programda gözlemlenebilir tüm bu nesnelerin farklı adresleri olmalıdır (paragraf 6). Bu, derleyicinin yerel bir statik olmayan sabit diziye sahip bir işlevin her çağrılması için yığın üzerinde yeni bir dizi oluşturmasını gerektirmez, çünkü derleyici, as-ifbu tür başka hiçbir nesnenin olamayacağını kanıtlayabilmesi şartıyla sığınabilir. gözlemledi.

Ne yazık ki, işlev önemsiz olmadığı sürece bunu kanıtlamak kolay olmayacaktır (örneğin, gövdesi çeviri biriminde görünür olmayan başka bir işlevi çağırmaz), çünkü tanım gereği az çok diziler adreslerdir. Bu nedenle, çoğu durumda, statik olmayan const(expr)dizi, her çağırmada yığın üzerinde yeniden oluşturulmalıdır, bu da derleme zamanında hesaplayabilme noktasını yener.

Öte yandan, yerel bir static constnesne tüm gözlemciler tarafından paylaşılır ve ayrıca tanımlandığı işlev asla çağrılmasa bile başlatılabilir. Dolayısıyla, yukarıdakilerin hiçbiri geçerli değildir ve bir derleyici yalnızca bunun tek bir örneğini oluşturmakta özgür değildir; salt okunur depolama alanında bunun tek bir örneğini oluşturmak ücretsizdir.

Bu yüzden kesinlikle static constexprörneğinizde kullanmalısınız .

Ancak, kullanmak istemeyeceğiniz bir durum var static constexpr. constexprBildirilen bir nesne ODR kullanılmadığı veya bildirilmediği sürece static, derleyici nesneyi hiç içermez. Bu oldukça faydalıdır, çünkü constexprderlenmiş programı gereksiz baytlarla kirletmeden derleme zamanı geçici dizilerinin kullanılmasına izin verir . Bu durumda, açıkça kullanımda istemem staticçünkü staticzamanında mevcut nesnenin zorlamak için muhtemeldir.


2
@ AndrewLazarus, constbir constnesneden uzaklaşamazsınız , sadece bir nesneyi const X*işaret eden bir nesneden uzaklaşamazsınız X. Ama mesele bu değil; önemli olan, otomatik nesnelerin statik adresleri olamaz. Söylediğim gibi, constexprderleme bittikten sonra anlamlı olmayı bırakıyor, bu yüzden atmak için hiçbir şey yok (ve muhtemelen hiçbir şey yok, çünkü nesnenin çalışma zamanında var olması bile garanti
edilmiyor

17
Bu cevabın inanılmaz derecede kafa karıştırıcı değil, aynı zamanda kendisiyle çelişkili olduğunu hissediyorum. Örneğin siz hemen hemen her zaman istediğini söylüyor staticve constexprfakat farklı şeyler yapıyor, dik ve bağımsız olduğunu açıklar. Daha sonra, ODR kullanımını göz ardı edeceği için (bu kullanışlı görünüyor) ikisini birleştirmemek için bir neden belirtin. Oh ve hala statik neden çalışma zamanı şeyler için olduğu için statik constexpr ile kullanılması gerektiğini görmüyorum. Constexpr ile statikin neden önemli olduğunu hiç açıklamadınız.
void.pointer

2
@ void.pointer: Son paragraf hakkında haklısın. Giriş bölümünü değiştirdim. Ben önemini açıkladı düşündüm static constexpr(sabit dizi her işlev çağrısında yeniden oluşturulmasını engellemektedir), ama daha net hale getirebilir bazı kelimeler tweaked. Teşekkürler.
rici

8
Derleme zamanı sabitleri ile çalışma zamanı sabitleri arasında değinmek de yararlı olabilir. Başka bir deyişle, constexprsabit bir değişken yalnızca derleme zamanı bağlamlarında kullanılıyorsa ve çalışma zamanında hiç gerekli değilse , o zaman staticmantıklı değildir, çünkü çalışma zamanına ulaştığınız noktaya kadar, değer etkili bir şekilde "satır içine alınmıştır". Ancak, constexprçalışma zamanı bağlamlarında kullanılırsa (diğer bir deyişle, örtük constexprolarak dönüştürülmesi constve çalışma zamanı kodu için fiziksel bir adresle kullanılabilir olması gerekir ) static, ODR uyumluluğunu vb. Sağlamak isteyecektir . En azından benim anlayışım budur.
void.pointer

3
Benim son yorum için bir örnek: static constexpr int foo = 100;. Kod gibi bir şey yapmadıkça , derleyicinin fooher yerde kelimenin tam anlamıyla yerini kullanamamasının bir nedeni yoktur . Yani üzerinde beri bu durumda hiçbir yararlılığını vardır zamanında yok. Yine derleyiciye kadar. 100&foostaticfoofoo
void.pointer

10

Bu sorunun yanıtına ek olarak, bu derleyici belirterek 's değerinde ilklendir için gerekli değildir constexprarasındaki fark bilerek, derleme zamanında değişken constexprve static constexprbu kullanmaktır static constexprdeğişken yalnızca bir kez başlatılır sağlamak.

Aşağıdaki kod, constexprdeğişkenin birden çok kez nasıl başlatıldığını gösterir (aynı değerle olsa da), ancak static constexpryalnızca bir kez başlatıldığını gösterir.

Buna ek olarak kod avantajı karşılaştırır constexprkarşı da constkombinasyon halinde static.

#include <iostream>
#include <string>
#include <cassert>
#include <sstream>

const short const_short = 0;
constexpr short constexpr_short = 0;

// print only last 3 address value numbers
const short addr_offset = 3;

// This function will print name, value and address for given parameter
void print_properties(std::string ref_name, const short* param, short offset)
{
    // determine initial size of strings
    std::string title = "value \\ address of ";
    const size_t ref_size = ref_name.size();
    const size_t title_size = title.size();
    assert(title_size > ref_size);

    // create title (resize)
    title.append(ref_name);
    title.append(" is ");
    title.append(title_size - ref_size, ' ');

    // extract last 'offset' values from address
    std::stringstream addr;
    addr << param;
    const std::string addr_str = addr.str();
    const size_t addr_size = addr_str.size();
    assert(addr_size - offset > 0);

    // print title / ref value / address at offset
    std::cout << title << *param << " " << addr_str.substr(addr_size - offset) << std::endl;
}

// here we test initialization of const variable (runtime)
void const_value(const short counter)
{
    static short temp = const_short;
    const short const_var = ++temp;
    print_properties("const", &const_var, addr_offset);

    if (counter)
        const_value(counter - 1);
}

// here we test initialization of static variable (runtime)
void static_value(const short counter)
{
    static short temp = const_short;
    static short static_var = ++temp;
    print_properties("static", &static_var, addr_offset);

    if (counter)
        static_value(counter - 1);
}

// here we test initialization of static const variable (runtime)
void static_const_value(const short counter)
{
    static short temp = const_short;
    static const short static_var = ++temp;
    print_properties("static const", &static_var, addr_offset);

    if (counter)
        static_const_value(counter - 1);
}

// here we test initialization of constexpr variable (compile time)
void constexpr_value(const short counter)
{
    constexpr short constexpr_var = constexpr_short;
    print_properties("constexpr", &constexpr_var, addr_offset);

    if (counter)
        constexpr_value(counter - 1);
}

// here we test initialization of static constexpr variable (compile time)
void static_constexpr_value(const short counter)
{
    static constexpr short static_constexpr_var = constexpr_short;
    print_properties("static constexpr", &static_constexpr_var, addr_offset);

    if (counter)
        static_constexpr_value(counter - 1);
}

// final test call this method from main()
void test_static_const()
{
    constexpr short counter = 2;

    const_value(counter);
    std::cout << std::endl;

    static_value(counter);
    std::cout << std::endl;

    static_const_value(counter);
    std::cout << std::endl;

    constexpr_value(counter);
    std::cout << std::endl;

    static_constexpr_value(counter);
    std::cout << std::endl;
}

Olası program çıktısı:

value \ address of const is               1 564
value \ address of const is               2 3D4
value \ address of const is               3 244

value \ address of static is              1 C58
value \ address of static is              1 C58
value \ address of static is              1 C58

value \ address of static const is        1 C64
value \ address of static const is        1 C64
value \ address of static const is        1 C64

value \ address of constexpr is           0 564
value \ address of constexpr is           0 3D4
value \ address of constexpr is           0 244

value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0
value \ address of static constexpr is    0 EA0

Gördüğünüz gibi constexpr, staticanahtar kelime başlatmanın yalnızca bir kez yapılmasını sağlarken, kendinizi birden çok kez (adres aynı değildir) başlatır.


constexpr_short yeniden başlatılırsa constexpr const short constexpr_shorthata vermek için kullanamayız
akhileshzmishra

sözdiziminiz bir constexpr constanlam ifade etmez , çünkü constexprzaten, bir veya daha fazla kez consteklemek constderleyici tarafından yok sayılır. Bir hata yakalamaya çalışıyorsunuz, ancak bu bir hata değil, çoğu derleyici böyle çalışır.
metablaster
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.