2013-06-08 21 views
5

Czy to jest legalne w języku C++ 11? Kompiluje się z najnowszym kompilatorem intel i wydaje się działać, ale po prostu mam wrażenie, że to jest fuks.Nowe miejsce docelowe na podstawie szablonu sizeof()

class cbase 
     { 
     virtual void call(); 
     }; 

template<typename T> class functor : public cbase 
    { 
    public: 
     functor(T* obj, void (T::*pfunc)()) 
      : _obj(obj), _pfunc(pfunc) {} 

     virtual void call() 
      { 
      (_obj)(*_pfunc)(); 
      } 
    private: 
     T& _obj; 
     void (T::*_pfunc)();    
     //edited: this is no good: 
     //const static int size = sizeof(_obj) + sizeof(_pfunc); 
    }; 

class signal 
    { 
    public: 
     template<typename T> void connect(T& obj, void (T::*pfunc)()) 
      { 
      _ptr = new (space) functor<T>(obj, pfunc); 
      } 
    private: 
     cbase* _ptr; 
     class _generic_object {}; 
     typename aligned_storage<sizeof(functor<_generic_object>), 
      alignment_of<functor<_generic_object>>::value>::type space; 
     //edited: this is no good: 
     //void* space[(c1<_generic_object>::size/sizeof(void*))]; 

    }; 

Konkretnie zastanawiam się, czy naprawdę jest void* space[(c1<_generic_object>::size/sizeof(void*))]; zamiar dać odpowiedni rozmiar dla obiektów członkowskich C1'S (_obj i _pfunc). (To nie jest).

EDIT: Więc po pewnym dalszych badań Wydaje się, że dodaje się będzie (więcej?) Popraw:

typename aligned_storage<sizeof(c1<_generic_object>), 
    alignment_of<c1<_generic_object>>::value>::type space; 

Jednak po inspekcji wygenerowany montaż, przy użyciu umieszczenie nowego w tym miejscu wydaje się hamować kompilator z optymalizacji dala wezwanie do „nowych” (co wydawało się zdarzyć podczas korzystania tylko regularne „_ptr = new c1;”

EDIT2: Zmieniono kod, aby intencje trochę jaśniejsze

+0

Jak to się ma nawet skompilować bez inicjalizatora dla referencyjnego elementu w 'c1'? Czy jest jakieś szczególne znaczenie dla tego referencyjnego członka, czy nie? – AnT

+0

Również, co jest ideą obliczania rozmiaru pamięci za pomocą 'sizeof (_obj)', która ocenia rozmiar całego obiektu (np. 'Sizeof T'), ale później konstruuje obiekt' c1 'w tym miejscu, który będzie fizycznie zawierają tylko * odwołanie * do 'some_type_t' (zaimplementowane jako wskaźnik)? – AnT

+0

Nie wiem, czy to się kompiluje, czy nie, jest to bardzo uproszczony przykład, który napisałem tutaj, aby przejść do odpowiednich części. Piszę klasę sygnał/oddzwanianie i chcę wyeliminować dynamiczne przydzielanie pamięci, jeśli to możliwe. –

Odpowiedz

3

.poda sumę rozmiarów członków, ale może to nie być to samo, co rozmiar klasy zawierającej tych członków. Kompilator może wstawiać dopełnienie między elementami lub po ostatnim elemencie. W związku z tym, łączenie rozmiarów elementów przybliża najmniejszy obiekt, jaki może być, ale niekoniecznie daje rozmiar obiektu z tymi elementami.

W rzeczywistości rozmiar obiektu może się różnić w zależności nie tylko od rodzaju jego członków, ale także od ich kolejności. Na przykład:

struct A { 
    int a; 
    char b; 
}; 

vs:

struct B { 
    char b; 
    int a; 
}; 

W wielu przypadkach A będą mniejsze niż B. W dokumencie A zwykle nie ma dopełnienia między a i b, ale w przypadku B, często występuje pewne dopełnianie (np. Z 4-bajtowym int, często występują 3 bajty wypełnienia między b i a).

Jako taka, Twoja space może nie zawierać wystarczającej ... przestrzeni do przechowywania obiektu, który próbujesz utworzyć tam w init.

+0

To dobra uwaga. Idealnie mógłbym przechowywać obiekty bezpośrednio w c2, ale ponieważ nie znam T, dopóki nie zostanie wywołana funkcja składowa, nie mogę tego zrobić, jak się wydaje. –

1

Myślę, że masz szczęście; Odpowiedź Jerry'ego wskazuje, że mogą pojawić się problemy z dopełnieniem. To, co myślę, że masz, to klasa inna niż wirtualna (tzn. Bez vtable), z zasadniczo dwoma wskaźnikami (pod maską).

Poza tym, arytmetyka: (c1<_generic_object>::size/sizeof(void*)) jest błędna, ponieważ będzie obciąć jeśli size jest nie wielokrotnością sizeof(void *). Czy trzeba coś takiego:

((c1<_generic_object>::size + sizeof(void *) - 1)/sizeof(void *))

1

ten kod nawet nie dostać się do kwestii dopełnienia, ponieważ ma kilka z nich bardziej bezpośrednich.

Klasa szablonu c1 jest zdefiniowana jako zawierająca element członkowski T &_obj typu referencyjnego. Zastosowanie sizeof do _obj w zakresie c1 spowoduje ocenę rozmiaru T, a nie rozmiaru samego elementu odniesienia.Nie jest możliwe uzyskanie fizycznego rozmiaru odwołania w C++ (przynajmniej bezpośrednio). Tymczasem każdy rzeczywisty obiekt typu c1<T> będzie fizycznie zawierać odniesienie do T, które jest zwykle realizowane w takich przypadkach, jak wskaźnik "pod maską".

Z tego powodu jest całkowicie dla mnie jasne, dlaczego wartość c1<_generic_object>::size służy jako miara pamięci wymaganej do budowy w-tempa rzeczywistego obiektu typu c1<T> (dla dowolnego T). To po prostu nie ma sensu. Te rozmiary nie są w ogóle powiązane.

Przy odrobinie szczęścia rozmiar pustej klasy _generic_object może być równy tej samej (lub większej) wartości co rozmiar fizycznej implementacji elementu referencyjnego. W takim przypadku kod przydzieli wystarczającą ilość pamięci. Ktoś mógłby nawet twierdzić, że równość "zwykle" utrzyma się w praktyce. Ale byłby to całkowicie przypadkowy przypadek, bez jakiejkolwiek sensownej podstawy.

To nawet wygląda jak śledź czerwony celowo wprowadzony do kodu w celu czystego zaciemniania.

P.S. W GCC sizeof pustej klasy faktycznie jest oceniany jako 1, a nie "wyrównany" rozmiar. Oznacza to, że powyższa technika gwarantuje zainicjowanie wartości c1<_generic_object>::size o zbyt małej wartości. Dokładniej, w 32-bitowym GCC wartość c1<_generic_object>::size będzie wynosić 9, podczas gdy rzeczywisty rozmiar dowolnego c1<some_type_t> będzie wynosił 12 bajtów.

Powiązane problemy