2011-12-06 19 views
6

mam zagadkę który próbuję rozwiązać, i zasadniczo sprowadza się do następującego przykładu:CRTP i rodzaj widoczność

template <typename CT> 
struct A 
{ 
    typedef typename CT::VALUE_T FOO; // FOO is dependent on CT 
}; 

template <typename CT> 
struct B 
{ 
    typedef typename CT::BAR BAR; 

    BAR foo() {} 
}; 

template <typename DT> 
struct C : B<C<DT> > 
{ 
    typedef DT VALUE_T; 

    typedef typename A<C>::FOO BAR; 
}; 

int main() { 
    C<int> c; 
} 

mogę spróbować wyjaśnić powyższe (próbowałem około trzy razy i usunięty tekst), ale w zasadzie wymagania są:

  1. C musi dziedziczyć z B wpisane z C (wydobywająca CRTP), tj B<C<>>
  2. C jest jedynym, który może utworzyć wystąpienie A (tj. A musi być wpisany z C)
  3. A jest jedynym, który może określić FOO (FOO jest zależna od rodzaju CT, związek jest bardziej skomplikowana niż przedstawiony)

Problem (jak można patrz powyższy kod) jest to, że typ BAR jest dostępny tylko w zakresie C i jest niekompletny, gdy B jest utworzony, dlatego B nie widzi typu BAR z argumentem szablonu CT (C<int>). Niestety w zakresie B, typ BAR jest używany jako argumenty funkcji i typy zwracane (to znaczy nie tylko ograniczone do zakresu funkcji - w rezultacie nie mogę po prostu przenieść zakresu typedef do funkcji).

Czy istnieje sposób obejścia tego? Nie mogę złamać wyżej wymienionych związków (chyba że w ostateczności). Przypuszczalnie przy pomocy C++ 11, mógłbym użyć auto i obejść potrzebę posiadania BAR typedef w B, jednak obecnie nie jest to jeszcze opcja.

EDYCJA: podążanie za komentarzem @bitmasks, trochę więcej informacji.

  1. Kod w A i B służy się w kilku plików binarnych w różnych sytuacjach, tylko wyjątkowa sytuacja w tym przypadku jest to, że C wywodzi B, w pozostałych przypadkach C posiada instancję czegoś pochodzącego z B.
  2. Argumenty szablonu można zmienić (w A i B), o ile mogą być domyślnie ustawione na wartości, które nie wymagają zmiany istniejących zastosowań A i B. Ten sam zestaw typów musi być dostępny jako domyślny parametr szablonu lub inny mechanizm.

Używam szablonów po prostu dlatego, że wymagam ścisłego sprzężenia i potrzebuję elastyczności, aby użyć kodu w różnych sytuacjach.

Opisy elementów:

  • A najlepiej można opisać jako kontener i FOO naprawdę jest iterator, co zawiera określony jest przez typedef parametru szablonu
  • B najlepiej można opisać jako klasa podstawowa, która zawiera zestaw funkcji, które są wywoływane przez niektóre komponenty posiadane przez instancję C. W poprzednich przypadkach składniki te zostały przekazane jako odniesienie do rzeczy pochodzących z B (i te rzeczy są również własnością C), w tym przypadku, zapewniam odwołanie do samej C.

Głównym powikłaniem wynika z dostępu do pojemnika A wcześniej związek między B i C że Cma instancję z B, ale teraz Cjest instancją z B - ta zmiana semantyki przerw sposób w jaki typy są wstrzykiwane do klas.

+0

Od dłuższego czasu myślałem o twoim dylemacie i myślę, że musisz podać więcej informacji, dlaczego robisz objazd przez te wszystkie aliasy i jakie zmiany w twoim przykładzie są dopuszczalne (np. Dodatkowe parametry, dodatkowe typy itp.). – bitmask

+0

@bitmask, dziękuję za poświęcony mi czas, dodałem więcej informacji, oprócz prawdziwego kodu (którego nie mogę opublikować, ponieważ jest bardzo wrażliwy), próbuję opisać sytuację w aktualizacji. Sedno problemu polega na tym, że typy muszą być dostępne, niezależnie od tego, czy relacja między 'C' i' B' jest * ma * lub * jest a *. – Nim

+0

@Nim: Bardzo mylące i ładnie odczytywane przez bitmask. Czy możesz powiedzieć, dlaczego jesteś w takiej sytuacji? – Arunmu

Odpowiedz

4

Myślę, że możesz walczyć z wymogiem cyklicznym typedef z domyślnymi parametrami szablonu. Poniższe działa zgodnie z przeznaczeniem (o ile dobrze rozumiem pytanie) i pozostawia się (prawie) wszystkie wolnościami oryginalnego kodu:

template <typename CT, typename CTVALUE_T = typename CT::VALUE_T> 
struct A 
{ 
    //typedef typename CT::VALUE_T FOO; // FOO is dependent on CT 
    typedef CTVALUE_T FOO; // FOO is dependent on CT 
}; 

template <typename CT, typename CTBAR = typename CT::BAR> 
struct B 
{ 
    //typedef typename CT::BAR BAR; 
    typedef CTBAR BAR; 

    BAR foo() {return 0;} 
}; 

template <typename DT> struct VALUE_T__from__DT { 
    typedef DT VALUE_T; 
}; 

template <typename DT, template <class T> class _C_ > struct BAR__from__DT { 
    typedef typename A<_C_<DT> , typename VALUE_T__from__DT<DT>::VALUE_T >::FOO BAR; 
}; 

template <typename DT> 
struct C : B<C<DT>, typename BAR__from__DT<DT, C >::BAR > 
{ 
    //typedef DT VALUE_T; 

    //typedef typename A<C>::FOO BAR; 
}; 

int main() { 
    C<int> c; 
    int x = c.foo(); 
    (void)x; 
    return 0; 
} 

nazwiskami pomocnika rodzaje szablonów są straszne, być może można znaleźć lepsze nazwy.

Podstawową sztuczką jest to, że definicja problematyczna typedef s jest umieszczana w oddzielnym metakontenerie (tj. Pojemniku na cechy), tak że można tam nadal wykonywać dowolny voodoo.

Skomentowałem niepotrzebne typy (ale zostawiłem je, abyś miał większą szansę na zorientowanie się, co tu robię).

Czy to pasuje do twoich potrzeb?

+0

Dobra odpowiedź. +1 :) – Arunmu

+1

@ArunMu: Dzięki. Chodzi o to, że nie jestem w stanie oprzeć się interesującej (pozornie banalnej, ale bez widocznych rozwiązań) zagadce. – bitmask

+0

+1, rzeczywiście - to wygląda dobrze, muszę zobaczyć, jak mogę teraz przetłumaczyć to na mój prawdziwy kod. Po prostu muszę upewnić się, że 'BAR__ od__DT :: BAR' jest takie samo jak' A :: FOO BAR' .. – Nim