Saya memiliki kelas seperti ini:
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
// more stuff
};
Biasanya saya ingin default (nol) menginisialisasi counts
array seperti yang ditunjukkan.
Di lokasi tertentu yang diidentifikasi oleh profil, bagaimanapun, saya ingin menekan inisialisasi array, karena saya tahu array akan ditimpa, tetapi kompiler tidak cukup pintar untuk mengetahuinya.
Apa cara idiomatis dan efisien untuk membuat konstruktor zero-arg "sekunder" seperti itu?
Saat ini, saya menggunakan kelas tag uninit_tag
yang dilewatkan sebagai argumen dummy, seperti:
struct uninit_tag{};
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
event_counts(uninit_tag) {}
// more stuff
};
Lalu saya memanggil konstruktor no-init seperti event_counts c(uninit_tag{});
ketika saya ingin menekan konstruksi.
Saya terbuka untuk solusi yang tidak melibatkan pembuatan kelas dummy, atau lebih efisien dalam beberapa cara, dll.
c++
performance
constructor
BeeOnRope
sumber
sumber
Jawaban:
Solusi yang sudah Anda miliki sudah benar, dan saya ingin melihat apakah saya sedang meninjau kode Anda. Ini seefisien mungkin, jelas dan ringkas.
sumber
uninit_tag
rasa baru di setiap tempat yang ingin saya gunakan idiom ini. Saya berharap sudah ada semacam indikator seperti itu, mungkin distd::
.no_init
tag proyek-lebar dan menggunakannya di semua kelas saya di mana diperlukan.std::piecewise_construct_t
danstd::in_place_t
. Tak satu pun dari mereka tampaknya masuk akal untuk digunakan di sini. Mungkin Anda ingin selalu mendefinisikan objek global tipe Anda, jadi Anda tidak perlu kawat gigi di setiap panggilan konstruktor. STL melakukan inistd::piecewise_construct
untukstd::piecewise_construct_t
.Jika badan konstruktor kosong, itu dapat dihilangkan atau default:
Kemudian inisialisasi default
event_counts counts;
akan meninggalkan inisialisasicounts.counts
(inisialisasi default adalah no-op di sini), dan inisialisasievent_counts counts{};
nilai akan nilai inisialisasicounts.counts
, secara efektif mengisinya dengan nol.sumber
int i;
kami menerima bahwa itu tidak diinisialisasi nol. Mungkin kita juga harus menerima yangevent_counts counts;
tidak diinisialisasi nol dan menjadikanevent_counts counts{};
standar baru kita.Saya suka solusi Anda. Anda mungkin juga mempertimbangkan bersarang struct dan variabel statis. Sebagai contoh:
Dengan variabel panggilan konstruktor tak diinisialisasi variabel statis mungkin tampak lebih nyaman:
Tentu saja Anda dapat memperkenalkan makro untuk menyimpan pengetikan dan menjadikannya lebih sebagai fitur yang sistematis
sumber
Saya pikir enum adalah pilihan yang lebih baik daripada kelas tag atau bool. Anda tidak perlu melewati instance dari struct dan jelas dari pemanggil opsi mana yang Anda dapatkan.
Kemudian membuat instance terlihat seperti ini:
Atau, untuk membuatnya lebih seperti pendekatan kelas tag, gunakan enum nilai tunggal alih-alih kelas tag:
Lalu hanya ada dua cara untuk membuat instance:
sumber
event_counts() : counts{} {}
counts
tanpa syarat, tetapi hanya ketikaINIT
diatur.bool
danenum
layak, tetapi kita harus menyadari bahwa menggunakan parameter alih-alih memiliki warna semantik yang agak berbeda. Dalam yang pertama Anda jelas parametrize objek, maka sikap inisialisasi / tidak diinisialisasi menjadi keadaannya, sedangkan meneruskan objek tag ke ctor lebih seperti meminta kelas untuk melakukan konversi. Jadi itu bukan IMO masalah pilihan sintaksis.event_counts() : counts{} {}
.counts
diinisialisasi denganstd::fill
kecualiNO_INIT
diminta. Menambahkan konstruktor default seperti yang Anda sarankan akan membuat dua cara berbeda dalam melakukan inisialisasi default, yang bukan ide bagus. Saya telah menambahkan pendekatan lain yang menghindari penggunaanstd::fill
.Anda mungkin ingin mempertimbangkan inisialisasi dua fase untuk kelas Anda:
Konstruktor di atas tidak menginisialisasi array ke nol. Untuk mengatur elemen array ke nol, Anda harus memanggil fungsi anggota
set_zero()
setelah konstruksi.sumber
std::function
argumen konstruktor dengan sesuatu yang mirip denganset_zero
argumen default. Anda kemudian akan melewati fungsi lambda jika Anda ingin array yang tidak diinisialisasi.Saya akan melakukannya seperti ini:
Kompiler akan cukup pintar untuk melewati semua kode saat Anda gunakan
event_counts(false)
, dan Anda bisa mengatakan dengan tepat apa yang Anda maksud alih-alih membuat antarmuka kelas Anda jadi aneh.sumber
event_counts(false)
, apa artinya itu? Anda tidak tahu tanpa kembali dan melihat nama parameter. Lebih baik setidaknya menggunakan enum, atau, dalam hal ini, kelas sentinel / tag seperti yang ditunjukkan dalam pertanyaan. Kemudian, Anda mendapatkan deklarasi yang lebih miripevent_counts(no_init)
,, yang jelas bagi semua orang dalam artinya.event_counts(bool initCountr = true)
.boost::parameter
dan menyerukanevent_counts(initCounts = false)
keterbacaanevent_counts(bool initCounts = true)
sebenarnya adalah konstruktor default, karena setiap parameter memiliki nilai default. Persyaratannya hanyalah callable tanpa menentukan argumen,event_counts ec;
tidak peduli apakah itu parameter-kurang atau menggunakan nilai default.Saya akan menggunakan subkelas hanya untuk menyimpan sedikit pengetikan:
Anda dapat menyingkirkan kelas dummy dengan mengubah argumen konstruktor yang tidak menginisialisasi
bool
atauint
atau sesuatu, karena tidak harus mnemonik lagi.Anda juga bisa menukar pewarisan sekitar dan mendefinisikan
events_count_no_init
dengan konstruktor default seperti yang disarankan Evg dalam jawaban mereka, dan kemudianevents_count
menjadi subclass:sumber
event_counts
, saya ingin jenisnyaevent_count
tidakevent_count_uninitialized
, jadi saya harus mengiris tepat pada konstruksi sepertievent_counts c = event_counts_no_init{};
, yang saya pikir menghilangkan sebagian besar penghematan dalam mengetik.event_count_uninitialized
objek adalahevent_count
objek. Itulah inti warisan, mereka bukan tipe yang sepenuhnya berbeda.ecu
untukec
bekerja, tapi tidak jalan di sekitar lainnya. Atau jika Anda menggunakan fungsi templat, mereka adalah tipe yang berbeda dan diakhiri dengan instantiasi yang berbeda walaupun perilaku tersebut pada akhirnya identik (dan terkadang tidak seperti, dengan anggota templat statis). Terutama dengan penggunaan yang berat dariauto
ini pasti dapat muncul dan membingungkan: Saya tidak ingin cara objek diinisialisasi secara permanen tercermin dalam tipenya.