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 expressionint 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(){constint 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>classlist{};constexprint sqr1(int arg){return arg * arg;}int sqr2(int arg){return arg * arg;}int main(){constint X =2;list<sqr1(X)> mylist1;// OK: sqr1 is constexprlist<sqr2(X)> mylist2;// wrong: sqr2 is not constexpr}
Kapan saya bisa / harus saya gunakan keduanya, constdan bersama - constexprsama?
A. Dalam deklarasi objek. Ini tidak perlu ketika kedua kata kunci merujuk ke objek yang sama untuk dideklarasikan. constexprtersirat const.
constexprconstint N =5;
sama dengan
constexprint N =5;
Namun, perhatikan bahwa mungkin ada situasi di mana masing-masing kata kunci merujuk ke bagian deklarasi yang berbeda:
staticconstexprint N =3;int main(){constexprconstint*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
constexprvoid f();
perlu dinyatakan sebagai
constexprvoid f()const;
di bawah C ++ 14 agar tetap dapat digunakan sebagai constfungsi.
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.
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.
constexprint max(){return INT_MAX;}// OKconstexprlong long_max(){return2147483647;}// OKconstexprbool get_val(){bool res =false;return res;}// error: body is not just a return statementconstexprint square(int x){return x * x;}// OK: compile-time evaluation only if x is a constant expressionconstint 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
{constexprint two();// constant-expression functionprivate:staticconstexprint sz;// constant-expression object};constexprint S::sz =256;enumDataPacket{Small= S::two(),// error: S::two() called before it was definedBig=1024};constexprint 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 constructorconstexpr complex(double r,double i): re(r), im(i){}// OK: empty body// constant-expression functionsconstexprdouble real(){return re;}constexprdouble imag(){return im;}private:double re;double im;};constexpr complex COMP(0.0,1.0);// creates a literal complexdouble x =1.0;constexpr complex cx1(x,0);// error: x is not a constant expressionconst complex cx2(x,1);// OK: runtime initializationconstexprdouble xx = COMP.real();// OK: compile-time initializationconstexprdouble 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.
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:
constint dmv =17;// dmv is a named constantint var =17;// var is not a constantconstexprdouble max1 =1.4*square(dmv);// OK if square(17) is a constant expressionconstexprdouble max2 =1.4∗square(var);// error : var is not a constant expressionconstdouble max3 =1.4∗square(var);//OK, may be evaluated at run timedouble sum(constvector<double>&);// sum will not modify its argument (§2.2.5)vector<double> v {1.2,3.4,4.5};// v is not a constantconstdouble s1 = sum(v);// OK: evaluated at run timeconstexprdouble 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:
constexprdouble square(double x){return x∗x;}
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).
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:
constint x1=10;constexprint 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.constint x1=10;// OK - known at compile time.constint x2=temp;// OK - known only at runtime.constexprint x3=10;// OK - known at compile time.constexprint 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:
constint size1=10;// OK - value known at compile time.constint size2=temp;// OK - value known only at runtime.constexprint 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.
constexprint 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.constexprint 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
{staticconstexprint 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.constexprint getvalue()const{return(value);}constexpr test3(intValue): value(Value){}};constexpr test3 x(100);// OK. Constructor is constexpr.intarray[x.getvalue()];// OK. x.getvalue() is constexpr and can be evaluated at compile time.
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;constint myAge{age};// worksconstexprint someAge{age};// error: age can only be resolved at runtime
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[]){constint 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 constexprint 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.
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>constexprint multiply (int x,int y){return x * y;}externconstint val = multiply(10,10);int main (){
std::cout << val;}
kompilasi ke
val:.long100//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>constint multiply (int x,int y){return x * y;}constint val = multiply(10,10);//constexpr is an errorint 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.
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 ++.
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.
constexpr
menciptakan konstanta waktu kompilasi;const
hanya berarti bahwa nilai tidak dapat diubah.boost/hana
perpustakaan dapat menjelaskan beberapaconstexpr
masalah di mana Anda dapat menggunakanconstexpr
dan di mana Anda tidak dapat: boost.org/doc/libs/1_69_0/libs/hana/doc/html/…constexpr
waktu itu :)Jawaban:
Arti dasar dan sintaksis
Kedua kata kunci dapat digunakan dalam deklarasi objek serta fungsi. Perbedaan mendasar ketika diterapkan pada objek adalah ini:
const
mendeklarasikan 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.constexpr
mendeklarasikan objek yang sesuai untuk digunakan dalam apa yang disebut Standar sebagai ekspresi konstan . Tetapi perhatikan bahwaconstexpr
itu bukan satu-satunya cara untuk melakukan ini.Ketika diterapkan pada fungsi , perbedaan dasarnya adalah ini:
const
hanya 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.constexpr
dapat 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 (†) :return
pernyataan statis, hanya satu pernyataan yang diizinkan. Dalam kasus konstruktor, hanya daftar inisialisasi, typedef, dan pernyataan statis yang diizinkan. (= default
dan= delete
juga diizinkan.)asm
deklarasi,goto
pernyataan, pernyataan dengan label selaincase
dandefault
, coba-blok, definisi variabel non-literal jenis, definisi variabel durasi penyimpanan statis atau thread, definisi variabel yang tidak melakukan inisialisasi.Ekspresi yang konstan
Seperti yang dikatakan di atas,
constexpr
mendeklarasikan 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:
Tapi perhatikan:
Mendeklarasikan sesuatu sebagai
constexpr
tidak 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:Ini dimungkinkan karena
N
, konstan dan diinisialisasi pada waktu deklarasi dengan literal, memenuhi kriteria untuk ekspresi konstan, bahkan jika tidak dinyatakanconstexpr
.Jadi kapan saya harus menggunakannya
constexpr
?Sebuah objek seperti
N
di atas dapat digunakan sebagai ekspresi konstan tanpa dinyatakanconstexpr
. Ini berlaku untuk semua objek yaitu:const
[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:Kapan saya bisa / harus saya gunakan keduanya,
const
dan bersama -constexpr
sama?A. Dalam deklarasi objek. Ini tidak perlu ketika kedua kata kunci merujuk ke objek yang sama untuk dideklarasikan.
constexpr
tersiratconst
.sama dengan
Namun, perhatikan bahwa mungkin ada situasi di mana masing-masing kata kunci merujuk ke bagian deklarasi yang berbeda:
Di sini,
NP
dinyatakan 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, keduanyaconstexpr
danconst
diperlukan:constexpr
selalu merujuk ke ekspresi yang dinyatakan (di siniNP
), sementaraconst
merujuk keint
(itu menyatakan pointer- to-const). Menghapusconst
akan membuat ekspresi ilegal (karena (a) pointer ke objek non-const tidak bisa menjadi ekspresi konstan, dan (b)&N
sebenarnya adalah pointer-to-constant).B. Dalam deklarasi fungsi anggota. Di C ++ 11,
constexpr
tersiratconst
, sedangkan di C ++ 14 dan C ++ 17 itu tidak terjadi. Fungsi anggota dideklarasikan di bawah C ++ 11 sebagaiperlu dinyatakan sebagai
di bawah C ++ 14 agar tetap dapat digunakan sebagai
const
fungsi.sumber
constexpr
fungsi 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 sebagaiconstexpr
akan selalu dievaluasi pada waktu kompilasi, itu bisa ditafsirkan dengan cara yang salah.constexpr
objek, bukan fungsi. Saya suka berpikirconstexpr
tentang objek sebagai memaksa kompilasi evaluasi nilai waktu, danconstexpr
pada fungsi yang memungkinkan fungsi untuk dievaluasi pada waktu kompilasi atau menjalankan waktu yang sesuai.mutable
dapat juga dimodifikasi olehconst
fungsi anggota.const
berlaku untuk variabel , dan mencegahnya agar tidak dimodifikasi dalam kode Anda.constexpr
memberitahu kompiler bahwa ekspresi ini menghasilkan nilai konstanta waktu kompilasi , sehingga dapat digunakan di tempat-tempat seperti panjang array, menugaskan keconst
variabel, 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.
sumber
Gambaran
const
menjamin bahwa suatu program tidak mengubah nilai objek . Namun,const
tidak menjamin jenis inisialisasi yang dialami objek.Mempertimbangkan:
Fungsi ini
max()
hanya mengembalikan nilai literal. Namun, karena penginisialisasi adalah panggilan fungsi,mx
mengalami inisialisasi runtime. Karenanya, Anda tidak dapat menggunakannya sebagai ekspresi konstan :constexpr
adalah 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 ,constexpr
memungkinkan 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.
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.Konstruktor ekspresi konstan
Sebuah konstruktor konstan ekspresi adalah konstruktor dinyatakan
constexpr
. 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.
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 ++ .
sumber
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:
Agar suatu fungsi dapat digunakan dalam ekspresi konstan, yaitu, dalam ekspresi yang akan dievaluasi oleh kompiler, itu harus didefinisikan constexpr .
Sebagai contoh:
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).
sumber
Keduanya
const
danconstexpr
bisa diterapkan pada variabel dan fungsi. Meskipun mereka mirip satu sama lain, pada kenyataannya mereka adalah konsep yang sangat berbeda.Keduanya
const
danconstexpr
berarti bahwa nilai-nilai mereka tidak dapat diubah setelah inisialisasi mereka. Jadi misalnya:Perbedaan utama antara
const
danconstexpr
adalah waktu ketika nilai inisialisasi mereka diketahui (dievaluasi). Sementara nilai-nilaiconst
variabel dapat dievaluasi pada waktu kompilasi dan runtime,constexpr
selalu dievaluasi pada waktu kompilasi. Sebagai contoh: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.
Jadi itu berarti:
Jadi
const
variabel dapat mendefinisikan kedua konstanta waktu kompilasi sepertisize1
itu dapat digunakan untuk menentukan ukuran array dan konstanta runtime sepertisize2
yang hanya diketahui saat runtime dan tidak dapat digunakan untuk menentukan ukuran array. Di sisi lainconstexpr
selalu mendefinisikan konstanta waktu kompilasi yang dapat menentukan ukuran array.Keduanya
const
danconstexpr
dapat diterapkan pada fungsi juga. Sebuahconst
fungsi harus menjadi fungsi anggota (metode, operator) aplikasi mana dariconst
cara kata kunci bahwa metode tersebut tidak dapat mengubah nilai-nilai dari anggota mereka (non-statis) bidang. Sebagai contoh.A
constexpr
adalah 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.Ngomong-ngomong,
constexpr
fungsinya adalah fungsi C ++ reguler yang bisa dipanggil walaupun argumen tidak konstan dilewatkan. Tetapi dalam hal ini Anda mendapatkan nilai-nilai non-constexpr.The
constexpr
dapat juga diterapkan pada fungsi anggota (metode), operator dan bahkan konstruktor. Contohnya.Sampel yang lebih 'gila'.
sumber
constexpr int
ada tetapi diejaconst int
Seperti @ 0x499602d2 sudah tunjukkan,
const
hanya memastikan bahwa nilai tidak dapat diubah setelah inisialisasi di mana seperticonstexpr
(diperkenalkan dalam C ++ 11) menjamin variabel adalah konstanta waktu kompilasi.Pertimbangkan contoh berikut (dari LearnCpp.com):
sumber
A
const int var
dapat secara dinamis diatur ke nilai saat runtime dan setelah diatur ke nilai itu, tidak lagi dapat diubah.A
constexpr int var
tidak 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:
Cuplikan di atas mengkompilasi dengan baik dan saya telah berkomentar yang menyebabkan kesalahan.
Gagasan kunci di sini untuk mencatat, adalah gagasan tentang
compile time
danrun time
. Inovasi baru telah diperkenalkan ke C ++ dimaksudkan untuk sebanyak mungkin** know **
hal-hal tertentu pada waktu kompilasi untuk meningkatkan kinerja saat runtime.sumber
Saya tidak berpikir salah satu jawaban benar-benar menjelaskan dengan tepat apa efek sampingnya, atau memang, apa itu.
constexpr
danconst
pada namespace / file-scope identik ketika diinisialisasi dengan literal atau ekspresi; tetapi dengan suatu fungsi,const
dapat diinisialisasi dengan fungsi apa pun, tetapiconstexpr
diinisialisasi oleh non-constexpr (fungsi yang tidak ditandai dengan constexpr atau ekspresi non constexpr) akan menghasilkan kesalahan kompilator. Keduanyaconstexpr
danconst
merupakan keterkaitan internal implisit untuk variabel (yah sebenarnya, mereka tidak bertahan untuk sampai ke tahap menghubungkan jika kompilasi -O1 dan lebih kuat, danstatic
tidak memaksa kompiler untuk memancarkan simbol penghubung internal (lokal) untukconst
atauconstexpr
ketika pada -O1 atau lebih kuat, satu-satunya waktu melakukan ini adalah jika Anda mengambil alamat variabel,const
danconstexpr
akan menjadi simbol internal kecuali dinyatakan denganextern
ieextern constexpr/const int i = 3;
perlu digunakan). Pada suatu fungsi,constexpr
membuat fungsi secara permanen tidak pernah mencapai tahap menghubungkan (terlepas dariextern
atauinline
dalam definisi atau -O0 atau -Ofast), sedangkanconst
tidak pernah melakukannya, danstatic
daninline
hanya memiliki efek ini pada -O1 dan di atas. Ketika aconst
/constexpr
variabel diinisialisasi oleh suatuconstexpr
fungsi, beban selalu dioptimalkan dengan flag optimasi apa pun, tetapi itu tidak pernah dioptimalkan jika fungsi hanyastatic
atauinline
, atau jika variabel bukan aconst
/constexpr
.Kompilasi standar (-O0)
kompilasi ke
Namun
Kompilasi ke
Ini jelas menunjukkan bahwa
constexpr
menyebabkan inisialisasiconst/constexpr
variabel file-lingkup terjadi pada waktu kompilasi dan tidak menghasilkan simbol global, sedangkan tidak menggunakannya menyebabkan inisialisasi terjadi sebelummain
saat 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 lainnyaconstexpr
fungsi untuk hasil yang sama.constexpr
pada suatu fungsi juga mencegah penggunaan apa pun yang tidak dapat dilakukan pada waktu kompilasi dalam fungsi; misalnya, panggilan ke<<
operator aktifstd::cout
.constexpr
pada 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 denganinline
di C; fungsi penugasan variabel blok-lingkup dapat diuraikan hanya dengan menggunakan optimasi -O1 tanpaconstexpr
pada C dan C ++.sumber
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.
sumber