Pytanie Czy istnieje uzasadnienie dla utworzenia tymczasowego pliku, który jest natychmiast niszczony i nie jest używany bezpośrednio w C ++?


Zainspirowany przez to pytanie. Załóżmy, że mam class Lock z domyślnym konstruktorem iw pewnym kodzie wpisuję następującą instrukcję:

Lock();

spowoduje to utworzenie tymczasowego obiektu class Lock i natychmiast go niszcząc. Oczywiście, stworzenie mógłby mieć pewne skutki uboczne i to by zmienić zachowanie programu, jednak wygląda to dość dziwnie.

Moim pierwszym przypuszczeniem jest to, że takie stwierdzenia, chociaż są całkowicie poprawne z punktu widzenia języka, prawdopodobnie zawierają logiczny błąd.

Czy są jakieś ważne zastosowania powyższego oświadczenia? Czy są jakieś znane i popularne idiomy, które zawierają takie stwierdzenia? Dlaczego miałbym chcieć takich stwierdzeń we właściwym programie?


16
2018-06-29 09:46


pochodzenie


jedyne co mogę wymyślić to zastąpienie dwóch jawnych wywołań funkcji jednym, gdzie oba wymagają tych samych argumentów, drugie wystąpienie nie wymagałoby podania parametrów (o ile referencje są zapisywane w tymczasowym). IMHO, powinno to być ostrzeżenie kompilatora przynajmniej ... - Nim
Wydawałoby mi się, że gdyby coś takiego było użyteczne, równie dobrze mógłby zostać osiągnięty dzięki darmowej funkcji, która byłaby zdecydowanie lepsza. - Kerrek SB


Odpowiedzi:


Nie wygląda to wcale dziwniej niż wezwanie do funkcji zwanej void Lock.

Nie dlatego, że sugeruję, abyś myślał o funkcjach, jako o śmiesznie nazwanych konstruktorach (niezależnie od tego, jaki jest ich typ zwracany), ale składnia jest celowo podobna.

Nie mogę na razie wymyślić dobrego powodu do stworzenia, ale nie użycia a Lock, ale:

LockSession(lock);

ma takie same efekty uboczne, jakich można się spodziewać po:

acquire_and_then_immediately_release(lock);

Jak to się mówi, bardzo rzadko się tego chce, więc może to wyglądać na zwykłego czytelnika jak błąd. Jeśli z jakiegoś dziwnego powodu jest to, czego chcesz i chcesz uniknąć zamieszania ludzi, możesz:

{
    LockSession session(lock);
    // release immediately
}

lub o to chodzi:

void acquire_and_then_immediately_release(Lock &lock) {
    LockSession(lock);
}

z takim samym skutkiem i mniejszą szansą na drapanie głowy, czy to naprawdę było to, co miałeś na myśli, czy też właśnie popełniłeś błąd polegający na tym, że zapomniałeś podać imię i przez to trzymać zamek przez mniej czasu niż powinieneś.

Dlaczego chcesz zdobyć, a następnie natychmiast zwolnić blokadę? Prawdopodobnie dlatego, że (ab) używasz go jako nieco swoistego semafora. Na przykład możesz utworzyć grupę wątków z zablokowaną blokadą, a jeśli pierwszą rzeczą jest każda z nich, to żadna z nich nie przejdzie przez to dopóki nie zostanie wykonane tworzenie nici (plus wszystko inne, co rodzic chce, aby wątki być w stanie zobaczyć), a rodzic zwolni blokadę. Prawdopodobnie istnieją lepsze przykłady obiektów, których efekty uboczne są znaczne.

Odsuwając się od skutków ubocznych, inną możliwością jest to, że jeśli chcesz sprawdzić, czy ciąg znaków jest prawidłowym kodem XML, ty mógłby pisać:

xml::dom::Document(mystring);

spodziewa się wyjątku dla nieprawidłowych danych. Ponownie, kod będzie bardziej czytelny, jeśli wywołasz dobrze nazwaną funkcję, która to robi. A skonstruowanie drzewa DOM nie jest najskuteczniejszym sposobem sprawdzania poprawności XML. Ale jeśli klasa jest tym, co już masz, to jest to najprostsza rzecz, która działa.

Przypuszczam, że problem polega na tym, że nazwa klasy rzadko opisuje skutki uboczne jej tworzenia i niszczenia. Więc samotny tymczasowy będzie wyglądał dziwnie z tego powodu.


14
2018-06-29 09:56





Podobna składnia tworzenia tymczasowego jest używana wiele razy w templatemetaprogramy lub boost rodzaj bibliotek. W rzeczywistości nie twórz tymczasowo, ale symuluj jego efekt.

na przykład is_base_of

Możesz zobaczyć składnię obiektu tymczasowego w sizeof() sztuczka.


1
2018-06-29 09:54



Nie wydaje mi się, żeby było to tymczasowe - sprawdza rozmiar rodzaj funkcji ... - Nim
@Nim, już wspomniałem o tym w pogrubienie. Symuluje efekt tymczasowy. - iammilind
ta konstrukcja jest czysto kompilacyjna, więc jak może być tymczasowo zaangażowana podczas kompilacji? - Nim
Obawiam się, że to nie jest związane. Traits i inni zwykle mają tylko statyczne elementy, wyliczenia i definicje typów; jak w zasadzie zwracasz uwagę (zdaje się, że nie ma pogrubienie jak twierdzisz; edytuj: och, dodałeś to później, nawet jeśli twoje oświadczenie mówi, że było tam od początku), nie odpowiadasz na pytanie. Nie ma również symulacji efektu za pomocą sztuczki sizeof-operator. - Sebastian Mach
Mam na myśli: co właściwie masz na myśli poprzez symulację efektów? - Sebastian Mach


Aby uciec od niebezpieczeństwa va_list style variadics, napisałem kod, który nazywacie tak:

MyFunc() << a << b << c;

Gdzie MyFunc jest zaimplementowana jako klasa z operator << i ~MyFunc() jest odpowiedzialny za wykonanie pożądanej pracy. Napisanie tego jest niezręczne MyFunc klasy, ale gdy już to zrobi, działa dobrze.

Tak więc, może być użyteczny wzorzec tworzenia obiektu tymczasowego, który działa tylko na jedną linię.


1
2018-03-29 22:55





Jeden przypadek użycia, który mogę teraz sądzić, jest związany z metaprogramowaniem tempalte.

Jak wiesz, nie możesz częściowo wyspecjalizować funkcji, podczas gdy możesz to zrobić na zajęciach. Aby obejść ten problem, można wdrożyć funkcję jako klasę wewnątrz konstruktora i częściowo specjalizować całą klasę, jak mu się podoba.

Ostatnio spotkałem się z takim problemem. Potrzebowałem funkcji rekurencyjnie nazywanej samą, ale chciałem, aby ta rekursja została rozwiązana podczas kompilacji. W moim przypadku użyłem funkcji statycznej wewnątrz takiej klasy, ponieważ często chcę, aby zwracała coś innego niż void.

Rozważmy następujący przykład:

#include <stdio.h>

template <typename T, int i>
struct megapower {
  megapower(T &val) {
    val*=val;
    {megapower<T,i-1> tmp(val);}
  }
};

template <typename T>
struct megapower<T,1> {
  megapower(T &val) {}
};

int main() {
  int v=2;
  {megapower<int,5> tmp(v);}
  printf("%d\n",v);
  return 0;
}

Będzie obliczać 2 ^ 2 ^ 2 ^ 2 ^ 2 = 65536.

P.S. Jak się okazuje, nie możesz pisać megapower<int,5>(v). Kompilator pomyśli, że próbujesz utworzyć nowy obiekt v tego typu.


0
2018-06-29 10:22



Wydaje się jednak możliwe napisanie funkcji szablonu otoki, która wykonuje tymczasową instancję (częściowo wyspecjalizowanej klasy) wewnątrz implementacji funkcji. I będzie można napisać megapower <int, 5> (v) jeśli megapower stanie się funkcją :) - user396672
To wydaje się świetnym sposobem na ukrycie tego tymczasowego bałaganu. Dzięki :) - CygnusX1


Prawa kompilatora w zakresie eliminowania tych zmiennych i ellide konstruktorów z efektami ubocznymi z pamięci.


-3
2018-06-29 09:55



Czy masz na myśli, że nie mogę polegać na tych efektach ubocznych? Czy istnieje sformułowanie w standardzie? - sharptooth
Nie prawda. Kopiowanie konstruktorów może odbywać się tylko w sytuacjach określonych w standardzie. Możliwe są również inne konstruktory z efektami ubocznymi (cóż, efektami ubocznymi są: zawsze jest zasada "jak gdyby"). - Steve Jessop