Saya ingin memiliki static const
char
array di kelas saya. GCC mengeluh dan mengatakan kepada saya bahwa saya harus menggunakan constexpr
, meskipun sekarang memberitahu saya itu referensi yang tidak ditentukan. Jika saya membuat array menjadi non-anggota maka kompilasi. Apa yang sedang terjadi?
// .hpp
struct foo {
void bar();
static constexpr char baz[] = "quz";
};
// .cpp
void foo::bar() {
std::string str(baz); // undefined reference to baz
}
c++
c++11
static-members
constexpr
Pubby
sumber
sumber
int
@MooingDuck. Ia berfungsi dengan baik sebagai non-anggota. Bukankah itu melanggar aturan juga?int
s curang. Sebagai non-anggota, itu tidak boleh diizinkan, kecuali aturan berubah untuk C ++ 11 (mungkin)Jawaban:
Tambahkan ke file cpp Anda:
Alasan: Anda harus memberikan definisi anggota statis serta deklarasi. Deklarasi dan penginisialisasi masuk ke dalam definisi kelas, tetapi definisi anggota harus terpisah.
sumber
decltype(foo::baz) constexpr foo::baz;
C ++ 17 memperkenalkan variabel inline
C ++ 17 memperbaiki masalah ini untuk
constexpr static
variabel anggota yang membutuhkan definisi out-of-line jika itu digunakan odr. Lihat bagian kedua dari jawaban ini untuk detail pra-C ++ 17.Proposal P0386 Inline Variables memperkenalkan kemampuan untuk menerapkan
inline
specifier ke variabel. Khususnya untuk kasus iniconstexpr
menyiratkaninline
untuk variabel anggota statis. Proposal itu mengatakan:dan memodifikasi [basic.def] p2:
dan tambahkan [depr.static_constexpr] :
C ++ 14 dan sebelumnya
Dalam C ++ 03, kami hanya diizinkan untuk menyediakan inisialisasi kelas untuk jenis integral integral atau enumerasi , dalam C ++ 11 menggunakan
constexpr
ini diperluas ke tipe literal .Dalam C ++ 11, kita tidak perlu memberikan definisi lingkup namespace untuk anggota statis
constexpr
jika tidak digunakan , kita dapat melihat ini dari draft bagian standar C ++ 119.4.2
[class.static.data] yang mengatakan ( tekankan tambang ke depan ):Jadi kemudian pertanyaannya adalah, apakah yang
baz
digunakan di sini:dan jawabannya adalah ya , jadi kami memerlukan definisi lingkup namespace juga.
Jadi bagaimana kita menentukan apakah suatu variabel digunakan odr ? C ++ 11 yang asli kata-kata di bagian
3.2
[basic.def.odr] mengatakan:Begitu
baz
juga menghasilkan ekspresi konstan tetapi konversi lvalue ke rvalue tidak segera diterapkan karena tidak berlaku karenabaz
menjadi array. Ini tercakup dalam bagian4.1
[conv.lval] yang mengatakan:Apa yang diterapkan dalam konversi array-to-pointer .
Kata-kata [basic.def.odr] ini diubah karena Laporan Cacat 712 karena beberapa kasus tidak dicakup oleh kata-kata ini tetapi perubahan ini tidak mengubah hasil untuk kasus ini.
sumber
constexpr
tidak ada hubungannya sama sekali dengan itu? (baz
adalah ekspresi yang konstan)integral or enumeration type
tetapi sebaliknya, ya, yang penting adalah bahwa itu adalah ekspresi yang konstan .Ini benar-benar cacat dalam C ++ 11 - seperti yang orang lain jelaskan, dalam C ++ 11 variabel anggota constexpr statis, tidak seperti setiap jenis variabel global constexpr lainnya, memiliki hubungan eksternal, sehingga harus secara eksplisit didefinisikan di suatu tempat.
Perlu juga dicatat bahwa dalam praktiknya Anda sering dapat lolos dengan variabel anggota constexpr statis tanpa definisi saat kompilasi dengan optimasi, karena mereka dapat berakhir dengan sebaris di semua penggunaan, tetapi jika Anda mengkompilasi tanpa optimasi sering kali program Anda akan gagal untuk ditautkan. Ini membuat ini jebakan tersembunyi yang sangat umum - program Anda mengkompilasi dengan baik dengan optimasi, tetapi segera setelah Anda mematikan optimasi (mungkin untuk debugging), ia gagal untuk terhubung.
Kabar baiknya - cacat ini diperbaiki di C ++ 17! Pendekatannya agak berbelit-belit: dalam C ++ 17, variabel anggota constexpr statis secara implisit sejalan . Memiliki inline diterapkan pada variabel adalah konsep baru dalam C ++ 17, tetapi secara efektif berarti bahwa mereka tidak memerlukan definisi eksplisit di mana saja.
sumber
Bukankah solusi yang lebih elegan mengubah
char[]
menjadi:Dengan cara ini kita dapat memiliki definisi / deklarasi / inisialisasi dalam 1 baris kode.
sumber
char[]
Anda dapat menggunakansizeof
untuk mendapatkan panjang string pada waktu kompilasi, denganchar *
Anda tidak bisa (itu akan mengembalikan lebar tipe pointer, 1 dalam kasus ini).sizeof
masalah ini, dan dapat digunakan dalam solusi "hanya header"Solusi saya untuk tautan eksternal anggota statis adalah dengan menggunakan
constexpr
getter anggota referensi (yang tidak mengalami masalah @gnzlbg yang dimunculkan sebagai komentar atas jawaban dari @deddebme).Ungkapan ini penting bagi saya karena saya tidak suka memiliki banyak file .cpp di proyek saya, dan mencoba membatasi jumlahnya menjadi satu, yang hanya terdiri dari
#include
s dan sebuahmain()
fungsi.sumber
Di lingkungan saya, gcc vesion adalah 5.4.0. Menambahkan "-O2" dapat memperbaiki kesalahan kompilasi ini. Tampaknya gcc dapat menangani kasus ini saat meminta optimasi.
sumber