Kapan Anda harus menggunakan kemampuan constexpr di C ++ 11?

337

Tampak bagi saya bahwa memiliki "fungsi yang selalu mengembalikan 5" adalah memecah atau melemahkan arti "memanggil fungsi". Pasti ada alasan, atau kebutuhan untuk kemampuan ini atau itu tidak akan ada di C ++ 11. Kenapa disana?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

Tampaknya bagi saya bahwa jika saya menulis sebuah fungsi yang mengembalikan nilai literal, dan saya datang ke tinjauan kode, seseorang akan memberi tahu saya, maka saya harusnya, menyatakan nilai konstan daripada menulis return 5.

Warren P
sumber
28
Bisakah Anda mendefinisikan fungsi rekursif yang mengembalikan a constexpr? Jika demikian, saya dapat melihat penggunaannya.
sebelum
20
Saya percaya bahwa pertanyaan harus menyatakan "mengapa memperkenalkan kata kunci baru (!) Jika kompiler dapat menyimpulkan sendiri apakah suatu fungsi dapat dievaluasi dalam waktu kompilasi atau tidak". Setelah itu "dijamin oleh kata kunci" kedengarannya bagus, tapi saya pikir saya lebih suka memilikinya dijamin kapan pun memungkinkan, tanpa perlu kata kunci.
Kos
6
@Kos: Seseorang yang LEBIH fasih dengan C ++ internal mungkin akan lebih suka pertanyaan Anda, tetapi pertanyaan saya berasal dari perspektif seseorang yang telah menulis kode C sebelumnya, tetapi sama sekali tidak akrab dengan kata kunci C ++ 2011 sama sekali, maupun detail implementasi kompiler C ++ . Mampu berargumentasi tentang optimisasi kompiler dan deduksi ekspresi konstan adalah subjek untuk pertanyaan pengguna yang lebih maju daripada yang ini.
Warren P
8
@ Jadi saya berpikir dengan cara yang sama seperti Anda, dan jawaban yang saya ajukan adalah, tanpa constexpr, bagaimana Anda (dengan mudah) tahu bahwa kompiler sebenarnya mengkompilasi-waktu-mengevaluasi fungsi untuk Anda? Saya kira Anda dapat memeriksa output perakitan untuk melihat apa yang dilakukannya, tetapi lebih mudah untuk hanya memberitahu kompiler bahwa Anda memerlukan optimasi itu, dan jika karena alasan tertentu tidak dapat melakukan itu untuk Anda, itu akan memberi Anda kompilasi yang bagus- kesalahan bukannya diam-diam gagal untuk mengoptimalkan di mana Anda diharapkan untuk mengoptimalkan.
Jeremy Friesner
3
@ Ko: Anda bisa mengatakan hal yang sama tentang const. Bahkan, niat diamanatkan adalah berguna ! Dimensi array adalah contoh kanonik.
Lightness Races in Orbit

Jawaban:

303

Misalkan itu melakukan sesuatu yang sedikit lebih rumit.

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

Sekarang Anda memiliki sesuatu yang dapat dievaluasi ke konstan sambil mempertahankan keterbacaan yang baik dan memungkinkan pemrosesan yang sedikit lebih kompleks daripada hanya menetapkan konstanta ke angka.

Ini pada dasarnya memberikan bantuan yang baik untuk pemeliharaan karena menjadi lebih jelas apa yang Anda lakukan. Ambil max( a, b )contoh:

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

Itu pilihan yang cukup sederhana di sana tetapi itu berarti bahwa jika Anda menelepon max dengan nilai konstan itu secara eksplisit dihitung pada waktu kompilasi dan bukan pada saat runtime.

Contoh bagus lainnya adalah DegreesToRadiansfungsi. Semua orang menemukan derajat lebih mudah dibaca daripada radian. Meskipun Anda mungkin tahu bahwa 180 derajat dalam radian, itu jauh lebih jelas ditulis sebagai berikut:

const float oneeighty = DegreesToRadians( 180.0f );

Banyak info bagus di sini:

http://en.cppreference.com/w/cpp/language/constexpr

Goz
sumber
18
Poin yang sangat bagus dengan itu memberitahu kompiler untuk mencoba dan menghitung nilai pada waktu kompilasi. Saya ingin tahu mengapa const tidak menyediakan fungsi ini ketika optimasi tertentu ditentukan? Atau apakah itu?
TamusJRoyce
11
@Tamus: Seringkali akan tetapi tidak wajib. constexpr mewajibkan kompiler dan akan mengeluarkan kesalahan jika tidak bisa.
Goz
20
Saya melihatnya sekarang. Dosa (0,5) adalah hal lain. Ini menggantikan makro C rapi.
Warren P
10
Saya dapat melihat ini sebagai pertanyaan wawancara baru: Jelaskan perbedaan antara kata kunci const dan constexpr.
Warren P
2
Sebagai cara mendokumentasikan poin ini untuk diri saya sendiri, saya menulis kode yang sama seperti di atas dan lagi dengan fungsi "const" daripada "constexpr". Karena saya menggunakan Clang3.3, -pedantic-errors dan -std = c ++ 11 saya berharap yang terakhir tidak akan dikompilasi. Itu dikompilasi dan dijalankan seperti dalam kasus "constexpr". Apakah Anda mengira ini adalah ekstensi dentang atau apakah ada perubahan pada spesifikasi C ++ 11 sejak posting ini dijawab?
Arbalest
144

pengantar

constexprtidak diperkenalkan sebagai cara untuk memberitahu implementasi bahwa sesuatu dapat dievaluasi dalam konteks yang membutuhkan ekspresi konstan ; implementasi yang sesuai telah dapat membuktikan ini sebelum C ++ 11.

Sesuatu yang tidak bisa dibuktikan oleh implementasi adalah maksud dari kode tertentu:

  • Apa yang ingin diungkapkan oleh pengembang dengan entitas ini?
  • Haruskah kita secara membolehkan kode digunakan dalam ekspresi-konstan , hanya karena itu berfungsi?

Apa jadinya dunia tanpa ini constexpr ?

Katakanlah Anda sedang mengembangkan perpustakaan dan menyadari bahwa Anda ingin dapat menghitung jumlah setiap bilangan bulat dalam interval (0,N].

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

Kurangnya niat

Kompiler dapat dengan mudah membuktikan bahwa fungsi di atas dapat dipanggil dalam ekspresi konstan jika argumen yang disampaikan diketahui selama terjemahan; tetapi Anda belum menyatakan ini sebagai maksud - itu terjadi begitu saja.

Sekarang orang lain datang, membaca fungsi Anda, melakukan analisis yang sama dengan kompiler; " Oh, fungsi ini dapat digunakan dalam ekspresi konstan!" , dan menulis potongan kode berikut.

T arr[f(10)]; // freakin' magic

Optimalisasi

Anda, sebagai pengembang perpustakaan "luar biasa" , memutuskan bahwa fakan menyimpan hasil cache ketika dipanggil; siapa yang ingin menghitung set nilai yang sama berulang kali?

int func (int n) { 
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ()) 
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

Hasil

Dengan memperkenalkan optimasi konyol Anda, Anda baru saja melanggar setiap penggunaan fungsi Anda yang kebetulan berada dalam konteks di mana ekspresi konstan diperlukan.

Anda tidak pernah berjanji bahwa fungsi tersebut dapat digunakan dalam ekspresi konstan , dan tanpa itu constexprtidak akan ada cara untuk memberikan janji seperti itu.


Jadi, mengapa kita perlu constexpr?

Penggunaan utama constexpr adalah untuk menyatakan niat .

Jika suatu entitas tidak ditandai sebagai constexpr- itu tidak pernah dimaksudkan untuk digunakan dalam ekspresi konstan ; dan bahkan jika itu benar, kami bergantung pada kompiler untuk mendiagnosis konteks tersebut (karena itu mengabaikan maksud kami).

Filip Roséen - ref
sumber
25
Ini mungkin jawaban yang benar, karena perubahan terbaru dalam C ++ 14 dan C ++ 17 memungkinkan rentang bahasa yang lebih luas untuk digunakan dalam constexprekspresi. Dengan kata lain, hampir semua hal dapat dianotasi constexpr(mungkin suatu hari akan hilang begitu saja karena ini?), Dan kecuali seseorang memiliki kriteria kapan akan digunakan constexpratau tidak, hampir semua kode akan ditulis seperti itu .
alecov
4
@alecov Jelas tidak semuanya ... I/O, syscalldan dynamic memory allocationpasti cann't ditandai sebagai constexprSelain itu, tidak semuanya harus menjadi constexpr.
JiaHao Xu
1
@alecov Beberapa fungsi dimaksudkan untuk dieksekusi saat runtime dan tidak ada artinya untuk melakukan ini pada waktu kompilasi.
JiaHao Xu
1
Saya juga suka jawaban ini yang terbaik. Kompilasi evaluasi waktu adalah optimalisasi yang rapi, tetapi apa yang benar-benar Anda dapatkan dari constexprjaminan adalah semacam perilaku. Sama seperti consthalnya.
Tomáš Zato - Reinstate Monica
Kompiler apa yang memungkinkan versi constexpr-less ini int f (int n) { return n > 0 ? n + f (n-1) : n;} T arr[f(10)]; saya tidak bisa mengkompilasi ini di mana saja?
Yer
91

Ambil std::numeric_limits<T>::max(): untuk alasan apa pun, ini adalah metode. constexprakan bermanfaat di sini.

Contoh lain: Anda ingin mendeklarasikan C-array (atau a std::array) yang sebesar array lain. Cara untuk melakukan ini saat ini adalah seperti ini:

int x[10];
int y[sizeof x / sizeof x[0]];

Tapi bukankah lebih baik menulis:

int y[size_of(x)];

Terima kasih constexpr, Anda dapat:

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}
Konrad Rudolph
sumber
1
+1 untuk penggunaan templat yang bagus, tapi itu akan bekerja sama persis tanpa constexpr, bukan?
Kos
21
@ Ko: Tidak. Ini akan mengembalikan nilai runtime. constexprmemaksa komplier untuk membuat fungsi mengembalikan nilai waktu kompilasi (jika bisa).
deft_code
14
@ Ko: tanpa constexpritu tidak dapat digunakan dalam deklarasi ukuran array, atau sebagai argumen template, terlepas dari apakah hasil dari panggilan fungsi adalah konstanta waktu kompilasi atau tidak. Keduanya pada dasarnya adalah satu-satunya use-case untuk constexprtetapi setidaknya argumen templat use-case agak penting.
Konrad Rudolph
2
"untuk alasan apa pun, ini adalah metode": Alasannya adalah bahwa hanya ada integer waktu kompilasi di C ++ 03, tetapi tidak ada tipe waktu kompilasi lainnya, jadi hanya metode yang dapat bekerja untuk tipe arbitrer sebelum C ++ 11.
Sebastian Mach
5
@ LwCui Tidak, ini bukan "ok": GCC hanya longgar secara default tentang hal-hal tertentu. Gunakan -pedanticopsi dan itu akan ditandai sebagai kesalahan.
Konrad Rudolph
19

constexprfungsinya sangat bagus dan tambahan yang bagus untuk c ++. Namun, Anda benar karena sebagian besar masalah yang dipecahkan dapat diatasi dengan makro secara tidak hati-hati.

Namun, salah satu penggunaan constexprtidak memiliki konstanta yang diketikkan C ++ 03, diketik.

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;
deft_code
sumber
12
Bisakah Anda mengklarifikasi bahwa "kesalahan linker yang sangat halus"? Atau setidaknya memberikan pointer ke klarifikasi?
enobayram
4
@ enobayram, operator ternary mengambil alamat operan. Itu tidak jelas dari kode. Semuanya mengkompilasi dengan baik, tetapi tautan gagal karena alamat dari fourtidak menyelesaikan. Saya harus benar-benar menggali untuk mencari siapa yang mengambil alamat static constvariabel saya .
deft_code
23
"Ini buruk karena alasan yang jelas": alasan yang paling jelas adalah titik koma, kan?
TonyK
4
"Kesalahan linker yang sangat halus" membuat saya benar-benar bingung. Baik fouratau fiveberada di lingkup.
Steven Lu
3
lihat juga enum classtipe baru , itu memperbaiki beberapa masalah enum.
ninMonkey
14

Dari apa yang saya baca, kebutuhan akan constexpr berasal dari masalah dalam metaprogramming. Kelas trait mungkin memiliki konstanta yang direpresentasikan sebagai fungsi, pikirkan: numeric_limits :: max (). Dengan constexpr, tipe-tipe fungsi tersebut dapat digunakan dalam metaprogramming, atau sebagai array array, dll.

Contoh lain dari bagian atas kepala saya adalah bahwa untuk antarmuka kelas, Anda mungkin ingin tipe turunan mendefinisikan konstanta mereka sendiri untuk beberapa operasi.

Edit:

Setelah mencari-cari di SO, sepertinya orang lain telah datang dengan beberapa contoh dari apa yang mungkin dilakukan dengan constexprs.

Lukas
sumber
"Untuk menjadi bagian dari antarmuka Anda harus menjadi fungsi"?
Daniel Earwicker
Sekarang saya bisa melihat manfaatnya, saya sedikit lebih bersemangat tentang C ++ 0x. Sepertinya hal yang dipikirkan dengan baik. Aku tahu pasti begitu. Standar bahasa itu uber-geeks jarang melakukan hal-hal acak.
Warren P
Saya jauh lebih bersemangat tentang lambdas, model threading, initializer_list, rvalue referensi, templat variadic, bind baru yang kelebihan ... ada cukup banyak yang dinanti-nantikan.
Lukas
1
Oh ya, tapi saya sudah mengerti lambdas / penutupan di beberapa bahasa lainnya. constexprlebih khusus berguna dalam kompiler dengan sistem evaluasi ekspresi kompilasi-waktu yang kuat. C ++ benar-benar tidak memiliki rekan di domain itu. (itu pujian yang kuat untuk C ++ 11, IMHO)
Warren P
11

Dari pidato Stroustrup di "Going Native 2012":

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s’
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2’ 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human

sumber
2
Contoh ini juga dapat ditemukan dalam makalah Stroustrup, Pengembangan Perangkat Lunak untuk Infrastruktur .
Matthieu Poullet
clang-3.3: error: tipe pengembalian fungsi constexpr 'Value <Second>' bukan tipe literal
Mitja
Ini bagus tapi siapa yang menempatkan literal dalam kode seperti ini. Memiliki kompiler Anda "periksa unit Anda" untuk Anda masuk akal jika Anda sedang menulis kalkulator interaktif.
bobobobo
5
@obobobo atau jika Anda menulis perangkat lunak navigasi untuk Mars Climate Orbiter, mungkin :)
Jeremy Friesner
1
Untuk membuatnya dikompilasi - 1. Gunakan garis bawah dalam sufiks literal. 2. tambahkan operator "" _m untuk 100_m. 3. gunakan 100.0_m, atau tambahkan kelebihan yang menerima lama tanpa tanda tangan. 4. Deklarasikan konstruktor Nilai konstruktor. 5. Tambahkan operator yang sesuai / ke kelas Value seperti ini: constexpr auto operator / (const Value <Y> & other) const {return Value <Unit <TheUnit :: m - Nilai <Y> :: TheUnit :: m, TheUnit :: kg - Nilai <Y> :: TheUnit :: kg, TheUnit :: s - Nilai <Y> :: TheUnit :: s >> (val / other.val); }. Di mana TheUnit diketik untuk Unit yang ditambahkan di dalam kelas Value.
0kcats
8

Penggunaan lain (belum disebutkan) adalah constexprkonstruktor. Ini memungkinkan pembuatan konstanta waktu kompilasi yang tidak harus diinisialisasi selama runtime.

const std::complex<double> meaning_of_imagination(0, 42); 

Pasangkan dengan literal yang ditentukan pengguna dan Anda memiliki dukungan penuh untuk kelas yang ditentukan pengguna literal.

3.14D + 42_i;
Motti
sumber
6

Dulu ada pola dengan metaprogramming:

template<unsigned T>
struct Fact {
    enum Enum {
        VALUE = Fact<T-1>*T;
    };
};

template<>
struct Fact<1u> {
    enum Enum {
        VALUE = 1;
    };
};

// Fact<10>::VALUE is known be a compile-time constant

Saya percaya constexprdiperkenalkan untuk membiarkan Anda menulis konstruksi seperti itu tanpa perlu template dan konstruksi aneh dengan spesialisasi, SFINAE, dan sebagainya - tetapi persis seperti Anda akan menulis fungsi run-time, tetapi dengan jaminan bahwa hasilnya akan ditentukan dalam kompilasi -waktu.

Namun, perhatikan bahwa:

int fact(unsigned n) {
    if (n==1) return 1;
    return fact(n-1)*n;
}

int main() {
    return fact(10);
}

Kompilasi dengan ini g++ -O3dan Anda akan melihat bahwa fact(10)memang dievakuasi pada waktu kompilasi!

Kompiler yang sadar VLA (jadi kompiler C dalam mode C99 atau kompiler C ++ dengan ekstensi C99) bahkan memungkinkan Anda untuk melakukan:

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

Tapi itu bukan C + + standar saat ini - constexprsepertinya cara untuk memerangi ini (bahkan tanpa VLA, dalam kasus di atas). Dan masih ada masalah perlunya memiliki ekspresi konstan "formal" sebagai argumen templat.

Kos
sumber
Fungsi fakta tidak dievaluasi pada waktu kompilasi. Itu harus berupa constexpr dan harus memiliki hanya satu pernyataan pengembalian.
Sumant
1
@Sumant: Anda benar bahwa itu tidak harus dievaluasi pada waktu kompilasi, tetapi itu benar! Saya merujuk pada apa yang sebenarnya terjadi pada kompiler. Kompilasi pada GCC baru-baru ini, lihat hasil asm dan periksa sendiri jika Anda tidak percaya padaku!
Kos
Coba tambahkan std::array<int, fact(2)>dan Anda akan melihat fakta () tidak dievaluasi pada waktu kompilasi. Hanya saja pengoptimal GCC melakukan pekerjaan dengan baik.
1
Itulah yang saya katakan ... apakah saya benar-benar tidak jelas? Lihat paragraf terakhir
Kos
5

Baru saja mulai beralih proyek ke c ++ 11 dan menemukan situasi yang sangat baik untuk constexpr yang membersihkan metode alternatif melakukan operasi yang sama. Titik kunci di sini adalah bahwa Anda hanya dapat menempatkan fungsi ke dalam deklarasi ukuran array ketika dinyatakan constexpr. Ada beberapa situasi di mana saya bisa melihat ini menjadi sangat berguna bergerak maju dengan bidang kode yang saya ikuti.

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}
jgibbs
sumber
4
Ini bisa juga ditulis: const size_t MaxIPV4StringLength = sizeof ("255.255.255.255");
Superfly Jon
static inline constexpr const automungkin lebih baik.
JiaHao Xu
3

Semua jawaban lainnya bagus, saya hanya ingin memberikan contoh keren dari satu hal yang dapat Anda lakukan dengan constexpr yang luar biasa. See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h ) adalah waktu kompilasi parser HTML dan mesin templat. Ini berarti Anda dapat memasukkan HTML dan keluar pohon yang dapat dimanipulasi. Setelah penguraian dilakukan pada waktu kompilasi dapat memberi Anda sedikit kinerja ekstra.

Dari contoh halaman github:

#include <iostream>
#include "seephit.h"
using namespace std;



int main()
{
  constexpr auto parser =
    R"*(
    <span >
    <p  color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p  >
    </span>
    )*"_html;

  spt::tree spt_tree(parser);

  spt::template_dict dct;
  dct["name"] = "Mary";
  dct["profession"] = "doctor";
  dct["city"] = "London";

  spt_tree.root.render(cerr, dct);
  cerr << endl;

  dct["city"] = "New York";
  dct["name"] = "John";
  dct["profession"] = "janitor";

  spt_tree.root.render(cerr, dct);
  cerr << endl;
}
Tenang
sumber
1

Contoh dasar Anda menyajikan argumen yang sama dengan konstanta itu sendiri. Mengapa menggunakan

static const int x = 5;
int arr[x];

lebih

int arr[5];

Karena cara itu lebih bisa dipertahankan. Menggunakan constexpr jauh lebih cepat untuk menulis dan membaca daripada teknik pemrograman yang ada.

Anak anjing
sumber
0

Itu dapat mengaktifkan beberapa optimisasi baru. constsecara tradisional adalah petunjuk untuk sistem tipe, dan tidak dapat digunakan untuk optimasi (misalnya constfungsi anggota dapat const_castdan memodifikasi objek, secara hukum, jadi consttidak dapat dipercaya untuk optimasi).

constexprberarti ekspresi benar - benar konstan, asalkan input ke fungsi adalah const. Mempertimbangkan:

class MyInterface {
public:
    int GetNumber() const = 0;
};

Jika ini diekspos di beberapa modul lain, kompiler tidak dapat mempercayai yang GetNumber()tidak akan mengembalikan nilai yang berbeda setiap kali dipanggil - bahkan secara berurutan tanpa ada panggilan non-const di antaranya - karena constbisa saja dibuang dalam implementasi. (Jelas setiap programmer yang melakukan ini harus ditembak, tetapi bahasa mengizinkannya, oleh karena itu kompiler harus mematuhi aturan.)

Menambahkan constexpr:

class MyInterface {
public:
    constexpr int GetNumber() const = 0;
};

Kompiler sekarang dapat menerapkan optimasi di mana nilai kembali GetNumber()cache dan menghilangkan panggilan tambahan GetNumber(), karena constexprmerupakan jaminan kuat bahwa nilai kembali tidak akan berubah.

AshleysBrain
sumber
Sebenarnya const dapat digunakan dalam optimasi ... Ini adalah perilaku yang tidak terdefinisi untuk mengubah nilai yang didefinisikan const bahkan setelah const_castIIRC. Saya berharap ini konsisten untuk constfungsi anggota, tetapi saya harus memeriksanya dengan standar. Ini berarti bahwa kompiler dapat dengan aman melakukan optimasi di sana.
Kos
1
@ Warren: tidak masalah jika optimasi benar-benar dilakukan, itu hanya diperbolehkan. @ Ko: ini adalah sedikit kehalusan yang diketahui bahwa jika objek asli tidak dinyatakan const ( int xvs const int x), maka aman untuk memodifikasinya dengan const_cast-menyingkirkan const pada pointer / referensi untuk itu. Jika tidak,const_cast akan selalu memunculkan perilaku yang tidak terdefinisi, dan menjadi sia-sia :) Dalam kasus ini, kompiler tidak memiliki informasi tentang kesesuaian objek asli, jadi ia tidak dapat mengatakannya.
AshleysBrain
@Kos Saya tidak berpikir const_cast adalah satu-satunya masalah di sini. Metode const diizinkan untuk membaca dan bahkan memodifikasi variabel global. Sebaliknya, seseorang dari utas lain juga dapat memodifikasi objek const antara panggilan.
enobayram
1
"= 0" tidak valid di sini dan harus dihapus. Saya akan melakukannya sendiri, tapi saya tidak yakin itu sesuai dengan protokol SO.
KnowItAllWannabe
Kedua contoh tidak valid: yang pertama ( int GetNumber() const = 0;) harus mendeklarasikan GetNumber()metode virtual. Kedua (constexpr int GetNumber() const = 0; ) tidak valid karena specifier murni ( = 0) menyiratkan metode menjadi virtual, tetapi constexpr tidak boleh virtual (ref: en.cppreference.com/w/cpp/language/constexpr )
stj
-1

Kapan harus menggunakan constexpr:

  1. setiap kali ada waktu kompilasi yang konstan.
BreakBadSP
sumber
Meskipun saya setuju dengan Anda, jawaban ini tidak menjelaskan mengapa constexpr lebih disukai daripada makro preprocessor atau const.
Sneftel
-3

Ini berguna untuk sesuatu seperti

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

int some_arr[MeaningOfLife()];

Ikat ini dengan kelas sifat atau sejenisnya dan itu menjadi sangat berguna.

banyak sekali
sumber
4
Dalam contoh Anda, ia menawarkan nol keuntungan di atas konstanta polos, sehingga tidak benar-benar menjawab pertanyaan.
Jalf
Ini adalah contoh yang dibuat-buat, bayangkan jika MeaningOfLife () mendapatkan nilainya dari tempat lain, ucapkan fungsi lain atau #define atau rangkaiannya. Anda mungkin tidak tahu apa yang dikembalikan, mungkin kode perpustakaan. Contoh lain, bayangkan sebuah wadah yang tidak dapat diubah yang memiliki metode constexpr size (). Anda sekarang dapat melakukan int arr [container.size ()];
Plivesey
2
@plivesey bisa tolong edit jawaban Anda dengan contoh yang lebih baik.
Mukesh