const vs constexpr pada variabel

303

Apakah ada perbedaan antara definisi berikut?

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

Jika tidak, gaya mana yang lebih disukai dalam C ++ 11?

fredoverflow
sumber
2
Superset: stackoverflow.com/questions/14116003/…
Ciro Santilli 郝海东 冠状 病 六四 六四 事件
Keduanya konstan waktu kompilasi. Tapi Anda bisa melakukan const_cast yang pertama dan menulisnya. Tapi itu akan dioptimalkan oleh kompiler karena ini tidak mempengaruhi "membaca" seperti yang terjadi pada waktu kompilasi.
Bonita Montero

Jawaban:

347

Saya percaya ada perbedaan. Mari kita ganti namanya sehingga kita bisa membicarakannya lebih mudah:

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

Kedua PI1dan PI2yang konstan, yang berarti Anda tidak bisa mengubah mereka. Namun hanya PI2 konstanta waktu kompilasi. Ini akan diinisialisasi pada waktu kompilasi. PI1dapat diinisialisasi pada waktu kompilasi atau waktu berjalan. Selain itu, hanya PI2 dapat digunakan dalam konteks yang memerlukan konstanta waktu kompilasi. Sebagai contoh:

constexpr double PI3 = PI1;  // error

tapi:

constexpr double PI3 = PI2;  // ok

dan:

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

tapi:

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

Seperti yang harus Anda gunakan? Gunakan mana yang memenuhi kebutuhan Anda. Apakah Anda ingin memastikan bahwa Anda memiliki konstanta waktu kompilasi yang dapat digunakan dalam konteks di mana konstanta waktu kompilasi diperlukan? Apakah Anda ingin dapat menginisialisasi dengan perhitungan yang dilakukan saat run time? Dll

Howard Hinnant
sumber
60
Apakah kamu yakin Karena const int N = 10; char a[N];berfungsi, dan batas array harus konstanta waktu kompilasi.
fredoverflow
10
Saya yakin sejauh contoh yang saya tulis (diuji masing-masing sebelum memposting). Namun kompiler saya tidak membiarkan saya mengkonversi PI1ke konstanta integral kompilasi-waktu untuk digunakan dalam array, tetapi tidak untuk digunakan sebagai parameter template integral non-tipe. Jadi konvertibilitas waktu kompilasi PI1menjadi tipe integral sepertinya sedikit hit & miss bagi saya.
Howard Hinnant
34
@ FredOverflow: Indeks array non-const telah "bekerja" selama sekitar satu dekade (ada misalnya ekstensi g ++ untuk itu), tetapi itu tidak berarti itu legal C ++ (meskipun beberapa standar C atau C ++ baru-baru ini membuatnya legal , saya lupa yang mana). Adapun perbedaan dalam konstanta compiletime, parameter template dan digunakan sebagai enumpenginisialisasi adalah dua perbedaan penting antara constdan constexpr(dan tidak berfungsi untuk apa pun double).
Damon
17
Paragraf 4 dari 5.19 Ekspresi konstan [expr.const] juga merupakan (non-normatif) catatan yang terkenal menguraikan bahwa implementasi diperbolehkan untuk melakukan aritmatika titik-mengambang berbeda (misalnya berkenaan dengan akurasi) pada waktu kompilasi daripada pada saat runtime. Jadi 1 / PI1dan 1 / PI2dapat menghasilkan hasil yang berbeda. Saya rasa teknisitas ini tidak sepenting saran dalam jawaban ini.
Luc Danton
4
Tapi itu constexpr double PI3 = PI1;bekerja dengan baik untukku. (MSVS2013 CTP). Apa yang saya lakukan salah?
NuPagadi
77

Tidak ada perbedaan di sini, tetapi penting ketika Anda memiliki tipe yang memiliki konstruktor.

struct S {
    constexpr S(int);
};

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

s0adalah konstanta, tetapi tidak berjanji untuk diinisialisasi pada waktu kompilasi. s1ditandai constexpr, sehingga konstanta dan, karena Skonstruktor juga ditandai constexpr, ia akan diinisialisasi pada waktu kompilasi.

Sebagian besar ini penting ketika inisialisasi saat runtime akan memakan waktu dan Anda ingin mendorong pekerjaan itu ke kompiler, di mana itu juga memakan waktu, tetapi tidak memperlambat waktu eksekusi dari program yang dikompilasi

Pete Becker
sumber
3
Saya setuju: kesimpulan yang saya dapatkan adalah yang constexprakan mengarah pada diagnosis seandainya perhitungan waktu kompilasi objek tidak mungkin dilakukan. Yang kurang jelas adalah apakah suatu fungsi yang mengharapkan parameter konstan dapat dieksekusi pada waktu kompilasi seandainya parameter tersebut dinyatakan constdan bukan sebagai constexpr: yaitu, akan constexpr int foo(S)dieksekusi pada waktu kompilasi jika saya memanggil foo(s0)?
Matthieu M.
4
@ MatthieuM: Saya ragu apakah foo(s0)akan dieksekusi pada waktu kompilasi, tetapi Anda tidak pernah tahu: kompiler diperbolehkan untuk melakukan optimasi seperti itu. Tentu saja, baik gcc 4.7.2 atau clang 3.2 tidak mengizinkan saya untuk mengkompilasiconstexpr a = foo(s0);
rici
50

constexpr menunjukkan nilai yang konstan dan dikenal selama kompilasi.
const menunjukkan nilai yang hanya konstan; tidak wajib mengetahui selama kompilasi.

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

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

Perhatikan bahwa const tidak menawarkan jaminan yang sama dengan constexpr, karena objek const tidak perlu diinisialisasi dengan nilai-nilai yang diketahui selama kompilasi.

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

Semua objek constexpr adalah const, tetapi tidak semua objek const adalah constexpr.

Jika Anda ingin kompiler menjamin bahwa variabel memiliki nilai yang dapat digunakan dalam konteks yang membutuhkan konstanta waktu kompilasi, alat untuk meraihnya adalah constexpr, bukan const.

Ajay yadav
sumber
2
Saya sangat menyukai penjelasan Anda..bisakah Anda bisa berkomentar lebih banyak tentang Di mana kasus-kasus yang mungkin perlu kita gunakan untuk mengkompilasi konstanta waktu dalam skenario kehidupan nyata.
Mayukh Sarkar
1
@MayukhSarkar Cukup Google C ++ mengapa constexpr , misalnya stackoverflow.com/questions/4748083/…
underscore_d
18

Sebuah constexpr simbolik konstan harus diberikan nilai yang diketahui pada waktu kompilasi. Sebagai contoh:

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

Untuk menangani kasus di mana nilai "variabel" yang diinisialisasi dengan nilai yang tidak diketahui pada waktu kompilasi tetapi tidak pernah berubah setelah inisialisasi, C ++ menawarkan bentuk konstanta kedua ( konstanta ). Sebagai contoh:

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

" Variabel const " seperti itu sangat umum karena dua alasan:

  1. C ++ 98 tidak memiliki constexpr, jadi orang menggunakan const .
  2. Daftar item "Variabel" yang bukan ekspresi konstan (nilainya tidak diketahui pada waktu kompilasi) tetapi tidak mengubah nilai setelah inisialisasi itu sendiri sangat berguna.

Referensi: "Pemrograman: Prinsip dan Praktek Menggunakan C ++" oleh Stroustrup

Jnana
sumber
25
Mungkin Anda seharusnya menyebutkan bahwa teks dalam jawaban Anda diambil kata demi kata dari "Pemrograman: Prinsip dan Praktek Menggunakan C ++" oleh Stroustrup
Aky