Perbedaan antara `constexpr` dan` const`

593

Apa perbedaan antara constexprdan const?

  • Kapan saya bisa menggunakan salah satunya saja?
  • Kapan saya bisa menggunakan keduanya dan bagaimana saya memilihnya?
MBZ
sumber
71
constexprmenciptakan konstanta waktu kompilasi; consthanya berarti bahwa nilai tidak dapat diubah.
0x499602D2
Mungkin artikel ini dari boost/hanaperpustakaan dapat menjelaskan beberapa constexprmasalah di mana Anda dapat menggunakan constexprdan di mana Anda tidak dapat: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…
Andry
@ 0x499602D2 " hanya berarti nilai yang tidak dapat diubah " Untuk skalar diinisialisasi dengan literal, satu nilai yang tidak bisa berubah adalah juga waktu kompilasi konstan.
curiousguy
@curiousguy Ya komentar saya sangat disederhanakan. Harus diakui saya juga baru pada constexprwaktu itu :)
0x499602D2

Jawaban:

587

Arti dasar dan sintaksis

Kedua kata kunci dapat digunakan dalam deklarasi objek serta fungsi. Perbedaan mendasar ketika diterapkan pada objek adalah ini:

  • constmendeklarasikan objek sebagai konstan . Ini menyiratkan jaminan bahwa setelah diinisialisasi, nilai objek itu tidak akan berubah, dan kompiler dapat menggunakan fakta ini untuk optimasi. Ini juga membantu mencegah programmer dari menulis kode yang memodifikasi objek yang tidak dimaksudkan untuk diubah setelah inisialisasi.

  • constexprmendeklarasikan objek yang sesuai untuk digunakan dalam apa yang disebut Standar sebagai ekspresi konstan . Tetapi perhatikan bahwa constexpritu bukan satu-satunya cara untuk melakukan ini.

Ketika diterapkan pada fungsi , perbedaan dasarnya adalah ini:

  • consthanya dapat digunakan untuk fungsi anggota yang tidak statis, bukan fungsi secara umum. Ini memberikan jaminan bahwa fungsi anggota tidak mengubah anggota data yang tidak statis.

  • constexprdapat digunakan dengan fungsi anggota dan non-anggota, serta konstruktor. Ini menyatakan fungsi yang cocok untuk digunakan dalam ekspresi konstan . Kompiler hanya akan menerimanya jika fungsinya memenuhi kriteria tertentu (7.1.5 / 3,4), yang terpenting (†) :

    • Badan fungsi harus non-virtual dan sangat sederhana: Terlepas dari typedefs dan returnpernyataan statis, hanya satu pernyataan yang diizinkan. Dalam kasus konstruktor, hanya daftar inisialisasi, typedef, dan pernyataan statis yang diizinkan. ( = defaultdan = deletejuga diizinkan.)
    • Pada C ++ 14, aturan lebih santai, apa yang diizinkan sejak saat itu di dalam fungsi constexpr: asmdeklarasi, gotopernyataan, pernyataan dengan label selain casedan default, coba-blok, definisi variabel non-literal jenis, definisi variabel durasi penyimpanan statis atau thread, definisi variabel yang tidak melakukan inisialisasi.
    • Argumen dan tipe pengembalian harus tipe literal (yaitu, secara umum, tipe yang sangat sederhana, biasanya skalar atau agregat)

Ekspresi yang konstan

Seperti yang dikatakan di atas, constexprmendeklarasikan kedua objek serta fungsinya sesuai untuk digunakan dalam ekspresi konstan. Ekspresi konstan lebih dari sekadar konstan:

  • Ini dapat digunakan di tempat-tempat yang memerlukan evaluasi waktu kompilasi, misalnya, parameter templat dan penentu ukuran array:

    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
  • Tapi perhatikan:

    • Mendeklarasikan sesuatu sebagai constexprtidak selalu menjamin bahwa itu akan dievaluasi pada waktu kompilasi. Dapat digunakan untuk itu, tetapi juga dapat digunakan di tempat lain yang dievaluasi pada saat run-time.

    • Objek mungkin cocok untuk digunakan dalam ekspresi konstan tanpa dideklarasikan constexpr. Contoh:

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

    Ini dimungkinkan karena N, konstan dan diinisialisasi pada waktu deklarasi dengan literal, memenuhi kriteria untuk ekspresi konstan, bahkan jika tidak dinyatakan constexpr.

Jadi kapan saya harus menggunakannya constexpr?

  • Sebuah objek seperti Ndi atas dapat digunakan sebagai ekspresi konstan tanpa dinyatakan constexpr. Ini berlaku untuk semua objek yaitu:

    • const
    • dari tipe integral atau enumerasi dan
    • diinisialisasi pada waktu deklarasi dengan ekspresi yang merupakan ekspresi konstan

    [Ini disebabkan oleh §5.19 / 2: Ekspresi konstan tidak boleh menyertakan subekspresi yang melibatkan "modifikasi nilai-ke-nilai kecuali jika [...] glvalue tipe integral atau enumerasi [...]" Terima kasih kepada Richard Smith untuk mengoreksi saya klaim sebelumnya bahwa ini berlaku untuk semua jenis literal.]

  • Agar suatu fungsi cocok untuk digunakan dalam ekspresi konstan, ia harus dinyatakan secara eksplisit constexpr; tidak cukup hanya memenuhi kriteria untuk fungsi ekspresi konstan. Contoh:

    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
    }

Kapan saya bisa / harus saya gunakan keduanya, constdan bersama - constexpr sama?

A. Dalam deklarasi objek. Ini tidak perlu ketika kedua kata kunci merujuk ke objek yang sama untuk dideklarasikan. constexprtersirat const.

constexpr const int N = 5;

sama dengan

constexpr int N = 5;

Namun, perhatikan bahwa mungkin ada situasi di mana masing-masing kata kunci merujuk ke bagian deklarasi yang berbeda:

static constexpr int N = 3;

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

Di sini, NPdinyatakan sebagai ekspresi konstan alamat, yaitu pointer yang dengan sendirinya merupakan ekspresi konstan. (Ini dimungkinkan ketika alamat dihasilkan dengan menerapkan operator alamat ke ekspresi konstan statis / global.) Di sini, keduanya constexprdan constdiperlukan: constexprselalu merujuk ke ekspresi yang dinyatakan (di sini NP), sementara constmerujuk ke int(itu menyatakan pointer- to-const). Menghapus constakan membuat ekspresi ilegal (karena (a) pointer ke objek non-const tidak bisa menjadi ekspresi konstan, dan (b) &Nsebenarnya adalah pointer-to-constant).

B. Dalam deklarasi fungsi anggota. Di C ++ 11, constexprtersirat const, sedangkan di C ++ 14 dan C ++ 17 itu tidak terjadi. Fungsi anggota dideklarasikan di bawah C ++ 11 sebagai

constexpr void f();

perlu dinyatakan sebagai

constexpr void f() const;

di bawah C ++ 14 agar tetap dapat digunakan sebagai constfungsi.

jogojapan
sumber
3
IMO "tidak harus dievaluasi pada waktu kompilasi" kurang membantu daripada menganggap mereka sebagai "dievaluasi pada waktu kompilasi". Kendala pada ekspresi konstan berarti bahwa relatif mudah bagi kompilator untuk mengevaluasinya. Seorang kompiler harus mengeluh jika kendala-kendala itu tidak dipenuhi. Karena tidak ada efek samping, Anda tidak akan pernah bisa membedakan apakah kompiler "mengevaluasi" atau tidak.
aschepler
10
@aschepler Tentu. Poin utama saya di sana adalah bahwa jika Anda memanggil suatu constexprfungsi pada ekspresi yang tidak konstan, misalnya variabel biasa, ini sah secara hukum dan fungsi tersebut akan digunakan seperti fungsi lainnya. Itu tidak akan dievaluasi pada waktu kompilasi (karena tidak bisa). Mungkin Anda berpikir itu sudah jelas - tetapi jika saya menyatakan bahwa fungsi yang dideklarasikan sebagai constexprakan selalu dievaluasi pada waktu kompilasi, itu bisa ditafsirkan dengan cara yang salah.
jogojapan
5
Ya, saya berbicara tentang constexprobjek, bukan fungsi. Saya suka berpikir constexprtentang objek sebagai memaksa kompilasi evaluasi nilai waktu, dan constexprpada fungsi yang memungkinkan fungsi untuk dievaluasi pada waktu kompilasi atau menjalankan waktu yang sesuai.
aschepler
2
Koreksi: 'const' hanya merupakan batasan bahwa ANDA tidak dapat mengubah nilai suatu variabel; itu tidak membuat janji bahwa nilainya tidak akan berubah (yaitu, oleh orang lain). Ini properti tulis, bukan properti baca.
Jared Grubb
3
Kalimat ini: Ini memberikan jaminan bahwa fungsi anggota tidak mengubah anggota data yang tidak statis. melewatkan satu detail penting. Anggota yang ditandai mutabledapat juga dimodifikasi oleh constfungsi anggota.
Mahakuasa
119

constberlaku untuk variabel , dan mencegahnya agar tidak dimodifikasi dalam kode Anda.

constexprmemberitahu kompiler bahwa ekspresi ini menghasilkan nilai konstanta waktu kompilasi , sehingga dapat digunakan di tempat-tempat seperti panjang array, menugaskan ke constvariabel, dll. Tautan yang diberikan oleh Oli memiliki banyak contoh bagus.

Pada dasarnya mereka adalah 2 konsep yang berbeda sekaligus, dan dapat (dan harus) digunakan bersama.

Karthik T
sumber
2
penggunaan const & constexpr, mis: en.cppreference.com/w/cpp/container/array/get
Manohar Reddy Poreddy
64

Gambaran

  • constmenjamin bahwa suatu program tidak mengubah nilai objek . Namun, consttidak menjamin jenis inisialisasi yang dialami objek.

    Mempertimbangkan:

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

    Fungsi ini max()hanya mengembalikan nilai literal. Namun, karena penginisialisasi adalah panggilan fungsi, mxmengalami inisialisasi runtime. Karenanya, Anda tidak dapat menggunakannya sebagai ekspresi konstan :

    int arr[mx];  // error: “constant expression required”
  • constexpradalah kata kunci C ++ 11 baru yang mengarahkan Anda pada kebutuhan untuk membuat makro dan literal yang telah dikodekan dengan keras. Ini juga menjamin, dalam kondisi tertentu, bahwa objek mengalami inisialisasi statis . Ini mengontrol waktu evaluasi suatu ekspresi. Dengan menerapkan evaluasi waktu kompilasi dari ekspresinya , constexprmemungkinkan Anda menentukan ekspresi konstan sejati yang penting untuk aplikasi kritis waktu, pemrograman sistem, templat, dan secara umum, dalam kode apa pun yang bergantung pada konstanta waktu kompilasi.

Fungsi ekspresi konstan

Sebuah fungsi konstan ekspresi adalah fungsi dideklarasikan constexpr. Badannya harus non-virtual dan terdiri dari pernyataan pengembalian tunggal saja, selain dari typedefs dan pernyataan statis. Argumen dan nilai pengembaliannya harus memiliki tipe literal. Ini dapat digunakan dengan argumen ekspresi tidak konstan, tetapi ketika itu dilakukan hasilnya bukan ekspresi konstan.

Fungsi ekspresi konstan dimaksudkan untuk mengganti makro dan literal yang dikodekan dengan keras tanpa mengorbankan kinerja atau keamanan tipe.

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)

Objek ekspresi konstan

Sebuah objek konstan ekspresi adalah obyek dinyatakan constexpr. Ini harus diinisialisasi dengan ekspresi konstan atau nilai yang dibangun oleh konstruktor ekspresi konstan dengan argumen ekspresi konstan.

Objek ekspresi-konstan berperilaku seolah-olah dinyatakan const, kecuali bahwa itu memerlukan inisialisasi sebelum digunakan dan penginisialisasi harus berupa ekspresi konstan. Akibatnya, objek ekspresi konstan selalu dapat digunakan sebagai bagian dari ekspresi konstan lainnya.

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

Konstruktor ekspresi konstan

Sebuah konstruktor konstan ekspresi adalah konstruktor dinyatakanconstexpr . Itu dapat memiliki daftar inisialisasi anggota tetapi tubuhnya harus kosong, terlepas dari typedefs dan pernyataan statis. Argumennya harus memiliki tipe literal.

Konstruktor ekspresi-konstan memungkinkan kompiler untuk menginisialisasi objek pada waktu kompilasi, asalkan argumen konstruktor adalah semua ekspresi konstan.

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

Kiat-kiat dari buku Effective Modern C ++ oleh Scott Meyers tentang constexpr:

  • constexpr objek adalah const dan diinisialisasi dengan nilai-nilai yang diketahui selama kompilasi;
  • constexpr fungsi menghasilkan hasil waktu kompilasi ketika dipanggil dengan argumen yang nilainya diketahui selama kompilasi;
  • constexpr objek dan fungsi dapat digunakan dalam konteks yang lebih luas daripada non-constexpr objek dan fungsi;
  • constexpr adalah bagian dari antarmuka objek atau fungsi.

Sumber: Menggunakan constexpr untuk Meningkatkan Keamanan, Kinerja dan Enkapsulasi dalam C ++ .

zangw
sumber
Terima kasih atas contoh kode yang menunjukkan situasi yang berbeda. Sebesar beberapa penjelasan lainnya, saya menemukan bahwa melihat kode dalam tindakan jauh lebih bermanfaat dan dapat dimengerti. Ini benar-benar membantu memperkuat pemahaman saya tentang apa yang sedang terjadi.
RTHarston
35

Menurut buku "The C ++ Programming Language 4th Editon" oleh Bjarne Stroustrup
const : artinya kira-kira '' Saya berjanji untuk tidak mengubah nilai ini '' (§7.5). Ini digunakan terutama untuk menentukan antarmuka, sehingga data dapat dikirimkan ke fungsi tanpa takut akan dimodifikasi.
Compiler memberlakukan janji yang dibuat oleh const.
constexpr : artinya kira-kira '' untuk dievaluasi pada waktu kompilasi '' (§10.4). Ini digunakan terutama untuk menentukan konstanta, untuk memungkinkan
Sebagai contoh:

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

Agar suatu fungsi dapat digunakan dalam ekspresi konstan, yaitu, dalam ekspresi yang akan dievaluasi oleh kompiler, itu harus didefinisikan constexpr .
Sebagai contoh:

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


Untuk menjadi constexpr, suatu fungsi harus agak sederhana: hanya pernyataan kembali menghitung nilai. Fungsi constexpr dapat digunakan untuk argumen yang tidak konstan, tetapi ketika itu dilakukan hasilnya bukan ekspresi yang konstan. Kami mengizinkan fungsi constexpr dipanggil dengan argumen non-konstanta-ekspresi dalam konteks yang tidak memerlukan ekspresi konstan, sehingga kami tidak harus mendefinisikan fungsi dasarnya sama dua kali: sekali untuk ekspresi konstan dan satu kali untuk variabel.
Di beberapa tempat, ekspresi konstan diperlukan oleh aturan bahasa (misalnya, batas array (§2.2.5, §7.3), label kasus (§2.2.4, §9.4.2), beberapa argumen templat (§25.2), dan konstanta dideklarasikan menggunakan constexpr). Dalam kasus lain, evaluasi waktu kompilasi penting untuk kinerja. Secara independen dari masalah kinerja, gagasan tentang keabadian (suatu objek dengan keadaan yang tidak dapat diubah) adalah masalah desain yang penting (§10.4).

Mustafa Ekici
sumber
masih ada masalah kinerja. Tampaknya fungsi constexpr jika dievaluasi saat runtime mungkin lebih lambat daripada versi fungsi non-constexpr. Juga jika kita memiliki nilai konstan apakah kita lebih suka "const" atau "constexpr"? (lebih banyak pertanyaan gaya perakitan yang dihasilkan terlihat sama)
CoffeDeveloper
31

Keduanya constdan constexprbisa diterapkan pada variabel dan fungsi. Meskipun mereka mirip satu sama lain, pada kenyataannya mereka adalah konsep yang sangat berbeda.

Keduanya constdan constexprberarti bahwa nilai-nilai mereka tidak dapat diubah setelah inisialisasi mereka. Jadi misalnya:

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.

Perbedaan utama antara constdan constexpradalah waktu ketika nilai inisialisasi mereka diketahui (dievaluasi). Sementara nilai-nilai constvariabel dapat dievaluasi pada waktu kompilasi dan runtime, constexprselalu dievaluasi pada waktu kompilasi. Sebagai contoh:

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.

Keuntungan utama untuk mengetahui apakah nilainya diketahui pada waktu kompilasi atau runtime adalah kenyataan bahwa konstanta waktu kompilasi dapat digunakan kapan pun konstanta waktu kompilasi diperlukan. Sebagai contoh, C ++ tidak memungkinkan Anda untuk menentukan array-C dengan panjang variabel.

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

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

Jadi itu berarti:

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.

Jadi constvariabel dapat mendefinisikan kedua konstanta waktu kompilasi seperti size1itu dapat digunakan untuk menentukan ukuran array dan konstanta runtime seperti size2yang hanya diketahui saat runtime dan tidak dapat digunakan untuk menentukan ukuran array. Di sisi lain constexprselalu mendefinisikan konstanta waktu kompilasi yang dapat menentukan ukuran array.

Keduanya constdan constexprdapat diterapkan pada fungsi juga. Sebuah constfungsi harus menjadi fungsi anggota (metode, operator) aplikasi mana dari constcara kata kunci bahwa metode tersebut tidak dapat mengubah nilai-nilai dari anggota mereka (non-statis) bidang. Sebagai contoh.

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 constexpradalah konsep yang berbeda. Ini menandai fungsi (anggota atau non-anggota) sebagai fungsi yang dapat dievaluasi pada waktu kompilasi jika konstanta waktu kompilasi dilewatkan sebagai argumen mereka . Misalnya Anda bisa menulis ini.

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.

Ngomong-ngomong, constexprfungsinya adalah fungsi C ++ reguler yang bisa dipanggil walaupun argumen tidak konstan dilewatkan. Tetapi dalam hal ini Anda mendapatkan nilai-nilai non-constexpr.

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.

The constexprdapat juga diterapkan pada fungsi anggota (metode), operator dan bahkan konstruktor. Contohnya.

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

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


    }
};

Sampel yang lebih 'gila'.

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.
Timmy_A
sumber
Juga, di C, constexpr intada tetapi diejaconst int
curiousguy
8

Seperti @ 0x499602d2 sudah tunjukkan, consthanya memastikan bahwa nilai tidak dapat diubah setelah inisialisasi di mana seperti constexpr(diperkenalkan dalam C ++ 11) menjamin variabel adalah konstanta waktu kompilasi.
Pertimbangkan contoh berikut (dari LearnCpp.com):

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
Lokesh Meher
sumber
5

A const int vardapat secara dinamis diatur ke nilai saat runtime dan setelah diatur ke nilai itu, tidak lagi dapat diubah.

A constexpr int vartidak dapat diatur secara dinamis saat runtime, melainkan pada waktu kompilasi. Dan setelah diatur ke nilai itu, tidak bisa lagi diubah.

Ini adalah contoh yang bagus:

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
}

Cuplikan di atas mengkompilasi dengan baik dan saya telah berkomentar yang menyebabkan kesalahan.

Gagasan kunci di sini untuk mencatat, adalah gagasan tentang compile timedan run time. Inovasi baru telah diperkenalkan ke C ++ dimaksudkan untuk sebanyak mungkin ** know **hal-hal tertentu pada waktu kompilasi untuk meningkatkan kinerja saat runtime.

kebenaranadjustr
sumber
3

Saya tidak berpikir salah satu jawaban benar-benar menjelaskan dengan tepat apa efek sampingnya, atau memang, apa itu.

constexprdan constpada namespace / file-scope identik ketika diinisialisasi dengan literal atau ekspresi; tetapi dengan suatu fungsi, constdapat diinisialisasi dengan fungsi apa pun, tetapi constexprdiinisialisasi oleh non-constexpr (fungsi yang tidak ditandai dengan constexpr atau ekspresi non constexpr) akan menghasilkan kesalahan kompilator. Keduanya constexprdan constmerupakan keterkaitan internal implisit untuk variabel (yah sebenarnya, mereka tidak bertahan untuk sampai ke tahap menghubungkan jika kompilasi -O1 dan lebih kuat, dan statictidak memaksa kompiler untuk memancarkan simbol penghubung internal (lokal) untuk constatau constexprketika pada -O1 atau lebih kuat, satu-satunya waktu melakukan ini adalah jika Anda mengambil alamat variabel, constdan constexprakan menjadi simbol internal kecuali dinyatakan dengan externieextern constexpr/const int i = 3;perlu digunakan). Pada suatu fungsi, constexprmembuat fungsi secara permanen tidak pernah mencapai tahap menghubungkan (terlepas dari externatau inlinedalam definisi atau -O0 atau -Ofast), sedangkan consttidak pernah melakukannya, dan staticdan inlinehanya memiliki efek ini pada -O1 dan di atas. Ketika a const/ constexprvariabel diinisialisasi oleh suatu constexprfungsi, beban selalu dioptimalkan dengan flag optimasi apa pun, tetapi itu tidak pernah dioptimalkan jika fungsi hanya staticatau inline, atau jika variabel bukan a const/ constexpr.

Kompilasi standar (-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;
} 

kompilasi ke

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):
        . 
        . 
        . 

Namun

#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;
}

Kompilasi ke

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

Ini jelas menunjukkan bahwa constexprmenyebabkan inisialisasi const/constexprvariabel file-lingkup terjadi pada waktu kompilasi dan tidak menghasilkan simbol global, sedangkan tidak menggunakannya menyebabkan inisialisasi terjadi sebelum mainsaat runtime.

Kompilasi menggunakan -Ofast

Bahkan -Ofast tidak mengoptimalkan beban! https://godbolt.org/z/r-mhif , jadi Anda perlu constexpr


constexpr fungsi juga bisa dipanggil dari dalam lainnya constexpr fungsi untuk hasil yang sama. constexprpada suatu fungsi juga mencegah penggunaan apa pun yang tidak dapat dilakukan pada waktu kompilasi dalam fungsi; misalnya, panggilan ke <<operator aktif std::cout.

constexprpada lingkup blok berperilaku sama karena menghasilkan kesalahan jika diinisialisasi oleh fungsi non-constexpr; nilainya juga diganti segera.

Pada akhirnya, tujuan utamanya adalah seperti fungsi inline C, tetapi hanya efektif ketika fungsi tersebut digunakan untuk menginisialisasi variabel lingkup file (yang fungsi tidak dapat dilakukan pada C, tetapi mereka dapat pada C ++ karena memungkinkan inisialisasi dinamis file- variabel lingkup), kecuali fungsi tidak dapat mengekspor simbol global / lokal ke linker juga, bahkan menggunakan extern/static, yang Anda bisa dengan inlinedi C; fungsi penugasan variabel blok-lingkup dapat diuraikan hanya dengan menggunakan optimasi -O1 tanpa constexprpada C dan C ++.

Lewis Kelsey
sumber
Poin bagus di tautan. Bisakah itu dianggap lebih aman secara umum untuk menggunakan constexpr karena menghasilkan lebih sedikit kebocoran simbol?
Neil McGill
1
@NeilMcGill tidak benar-benar karena inline dan statis akan menyebabkan kompiler tidak memancarkan simbol lokal untuk dikalikan jika kompilasi menggunakan -O1 atau lebih kuat. Constexpr adalah satu-satunya yang mengoptimalkan beban untuk val, tetapi selain itu identik dengan meletakkan statis atau sebaris sebelum fungsi. Saya lupa sesuatu yang lain juga. Constexpr adalah satu-satunya kata kunci yang tidak memancarkan simbol untuk fungsi pada -O0, statis dan inline do
Lewis Kelsey
1

Pertama-tama, keduanya adalah kualifikasi dalam c ++. Variabel yang dinyatakan const harus diinisialisasi dan tidak dapat diubah di masa mendatang. Oleh karena itu umumnya variabel yang dinyatakan sebagai const akan memiliki nilai bahkan sebelum kompilasi.

Tapi, untuk constexpr itu agak berbeda.

Untuk constexpr, Anda dapat memberikan ekspresi yang dapat dievaluasi selama kompilasi program.

Jelas, variabel yang dinyatakan sebagai constexper tidak dapat diubah di masa depan seperti halnya const.

Subhash Malireddy
sumber