Mam taką klasę:
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
// more stuff
};
Zwykle chcę domyślnie (zero) zainicjować counts
tablicę, jak pokazano.
Jednak w wybranych lokalizacjach zidentyfikowanych przez profilowanie chciałbym powstrzymać inicjalizację tablicy, ponieważ wiem, że tablica wkrótce zostanie zastąpiona, ale kompilator nie jest wystarczająco inteligentny, aby to rozgryźć.
Jaki jest idiomatyczny i skuteczny sposób stworzenia takiego „wtórnego” konstruktora zero-arg?
Obecnie używam klasy znaczników, uninit_tag
która jest przekazywana jako fikcyjny argument, na przykład:
struct uninit_tag{};
struct event_counts {
uint64_t counts[MAX_COUNTERS];
event_counts() : counts{} {}
event_counts(uninit_tag) {}
// more stuff
};
Następnie wywołuję konstruktor bez inicjacji, tak jak event_counts c(uninit_tag{});
gdy chcę wyłączyć konstrukcję.
Jestem otwarty na rozwiązania, które nie wymagają stworzenia sztucznej klasy lub są w jakiś sposób bardziej wydajne itp.
źródło
Odpowiedzi:
Rozwiązanie, które już masz, jest poprawne i jest dokładnie tym, co chciałbym zobaczyć, gdybym sprawdzał twój kod. Jest tak wydajny, jak to możliwe, jasny i zwięzły.
źródło
uninit_tag
smak w każdym miejscu, w którym chcę używać tego idiomu. Miałem nadzieję, że istnieje już coś w rodzaju takiego wskaźnika, być może wstd::
.no_init
znacznik obejmujący cały projekt i używałbym go we wszystkich moich klasach, w których byłby potrzebny.std::piecewise_construct_t
istd::in_place_t
. Żaden z nich nie wydaje się rozsądny w użyciu tutaj. Być może chciałbyś zdefiniować globalny obiekt tego typu, który będzie zawsze używany, więc nie potrzebujesz nawiasów klamrowych w każdym wywołaniu konstruktora. STL robi to zstd::piecewise_construct
zastd::piecewise_construct_t
.Jeśli treść konstruktora jest pusta, można ją pominąć lub ustawić domyślnie:
Wtedy domyślna inicjalizacja
event_counts counts;
pozostawicounts.counts
niezainicjowaną (tutaj domyślna inicjalizacja nie jest możliwa ), a inicjalizacjaevent_counts counts{};
wartości spowoduje zainicjowanie wartościcounts.counts
, skutecznie wypełniając ją zerami.źródło
int i;
akceptujemy, że nie jest on inicjowany na zero. Być może powinniśmy również zaakceptować, żeevent_counts counts;
nie jest to inicjalizacja zerowa i ustawićevent_counts counts{};
nasz nowy domyślny.Podoba mi się twoje rozwiązanie. Być może rozważałeś także zagnieżdżoną strukturę i zmienną statyczną. Na przykład:
W przypadku zmiennej statycznej wywołanie niezainicjowanego konstruktora może wydawać się wygodniejsze:
Możesz oczywiście wprowadzić makro, aby zapisać pisanie i uczynić go bardziej systematyczną funkcją
źródło
Myślę, że wyliczanie jest lepszym wyborem niż klasa tagów lub bool. Nie musisz przekazywać wystąpienia struktury i od osoby dzwoniącej jasno wynika, którą opcję otrzymujesz.
Następnie tworzenie instancji wygląda następująco:
Lub, aby uczynić to bardziej zbliżonym do klasy tagów, użyj wyliczenia pojedynczej wartości zamiast klasy tag:
Istnieją tylko dwa sposoby utworzenia instancji:
źródło
event_counts() : counts{} {}
counts
bezwarunkowa, ale tylko wtedy, gdyINIT
jest ustawiona.bool
ienum
są przyzwoite, ale musimy mieć świadomość, że za pomocą parametru zamiast przeciążenia ma nieco inny odcień semantyczną. W poprzednim wyraźnie sparametryzujesz obiekt, stąd postawa zainicjalizowana / niezainicjowana staje się jego stanem, podczas gdy przekazanie obiektu znacznika do ctor jest bardziej jak poproszenie klasy o wykonanie konwersji. Więc nie jest to IMO kwestią wyboru składniowego.event_counts() : counts{} {}
.counts
inicjowana jest przez,std::fill
chyba żeNO_INIT
zostanie o to poproszona. Dodanie domyślnego konstruktora, tak jak sugerujesz, stworzyłoby dwa różne sposoby domyślnej inicjalizacji, co nie jest dobrym pomysłem. Dodałem inne podejście, które pozwala uniknąć używaniastd::fill
.Możesz rozważyć inicjację dwufazową dla swojej klasy:
Konstruktor powyżej nie inicjuje tablicy do zera. Aby ustawić elementy tablicy na zero, musisz wywołać funkcję elementu
set_zero()
po zakończeniu budowy.źródło
std::function
jako argument konstruktora z czymś podobnym doset_zero
argumentu domyślnego. Następnie przekazałbyś funkcję lambda, jeśli chcesz niezainicjowanej tablicy.Zrobiłbym to w ten sposób:
Kompilator będzie wystarczająco inteligentny, aby pominąć cały kod, gdy go używasz
event_counts(false)
, i możesz powiedzieć dokładnie, co masz na myśli, zamiast sprawić, że interfejs twojej klasy będzie tak dziwny.źródło
event_counts(false)
, co to oznacza? Nie masz pojęcia, nie wracając i nie patrząc na nazwę parametru. Lepiej przynajmniej użyć wyliczenia lub, w tym przypadku, klasy wartownika / tagu, jak pokazano w pytaniu. Następnie otrzymujesz deklarację bardziej podobnąevent_counts(no_init)
, co dla każdego jest oczywiste.event_counts(bool initCountr = true)
.boost::parameter
i wezwaćevent_counts(initCounts = false)
do odczytuevent_counts(bool initCounts = true)
faktycznie jest konstruktor domyślny, z powodu każdego parametru mającego wartość domyślną. Wymagane jest tylko, aby można było wywoływać je bez podawania argumentów,event_counts ec;
nie obchodzi go, czy jest ono bez parametrów, czy używa wartości domyślnych.Użyłbym podklasy, żeby zaoszczędzić trochę pisania:
Można pozbyć klasy manekina przez zmianę argument konstruktora nie do inicjalizacji
bool
lubint
czy coś, bo nie ma już być pamięciowy.Możesz także zamienić dziedzictwo i zdefiniować
events_count_no_init
za pomocą domyślnego konstruktora, takiego jak sugerowany w odpowiedzi Evg, a następnieevents_count
być podklasą:źródło
event_counts
, chcę, żeby była tego rodzajuevent_count
,event_count_uninitialized
więc nie powinienem, więc powinienem kroić naevent_counts c = event_counts_no_init{};
etapie budowy , co, jak sądzę, eliminuje większość oszczędności podczas pisania.event_count_uninitialized
obiekt jestevent_count
obiektem. To jest sedno dziedziczenia, nie są to zupełnie różne typy.ecu
doec
niego działa, ale nie na odwrót. Lub jeśli używasz funkcji szablonów, są to różne typy i kończą się różnymi instancjami, nawet jeśli zachowanie kończy się identyczne (a czasem nie będzie tak np. Ze statycznymi elementami szablonu). Zwłaszcza przy intensywnym użyciuauto
tego może z pewnością pojawić się i być mylące: nie chciałbym, aby sposób inicjalizacji obiektu został trwale odzwierciedlony w jego typie.