2013-08-24 10 views
5

Istnieje już duża liczba pytań na temat unique_ptr i niekompletnego typu na SO, ale żaden nie może dać mi pojęcia, dlaczego poniższe nie ma praca:C++ 11: unique_ptr narzeka na niekompletny typ, ale nie kiedy go zawijam

// error: ... std::pair<...>::second has incomplete type 
template<typename K, typename T> struct Impl { 
    typedef typename std::unordered_map<K,Impl<K,T>>::iterator iter_type; 
    std::unique_ptr<iter_type> ptr; 
    Impl() : ptr(new iter_type()) {} 
}; 

int main() { Impl<int,int>(); return 0; } 

natomiast następujące robi:

template<typename K, typename T> struct Impl { 
    struct Wrapper { 
    typedef typename std::unordered_map<K,Impl<K,T>>::iterator iter_type; 
    iter_type iter; 
    }; 

    std::unique_ptr<Wrapper> ptr; 
    Impl() : ptr(new Wrapper()) {} 
}; 

int main() { Impl<int,int>(); return 0; } 

nie widzę gdzie różnica techniczna jest: Jeśli std::pair<...>::second (czyli Impl<K,T>) był niekompletny do Impl w pierwszym przykładzie, to powinien być niekompletny t o Wrapper również w drugim. Ponadto, , gdy wystarczy zawinąć unique_ptr w strukturze, dlaczego istnieje ograniczenie dla pierwszego przypadku?

UPDATE:

Po odpowiedź Dietmar Kuhl za, myślę, że problem można sprowadzić do następujących:

template<typename K, typename T> struct Impl { 
    typename std::unordered_map<K,Impl<K,T>>::iterator ptr; 
}; 

vs

template<typename K, typename T> struct Impl { 
    struct Wrapper { 
     typename std::unordered_map<K,Impl<K,T>>::iterator iter; 
    }; 
    Wrapper *ptr; 
}; 

Odpowiedz

3

Problem w pierwszym przypadku niekompletny typ jest używany z std::unordered_map<K, Impl<K, T>: Aby ustalić, co jest iterator, części std::unordered_map<K, Impl<K, T> ne ed ma być utworzony w czasie, gdy Impl jest tylko zadeklarowane. std::unique_ptr<...> nie ma nic wspólnego z błędem. Możesz usunąć użycie iter_type jako typedef, aby sprawdzić, czy jest to typ.

Z drugiej strony, po zawinięciu użycia typu iteratora na Wrapper, ten typ zagnieżdżony nie jest używany przed implementacją konstruktora. Oczywiście, inline zdefiniowane funkcje zachowują się tak, jakby klasa była po prostu w pełni zdefiniowane i są one realizowane tylko poza definicją klasy, tj powyższy kod jest równoważne

template<typename K, typename T> struct Impl { 
    struct Wrapper { 
    typedef typename std::unordered_map<K,Impl<K,T>>::iterator iter_type; 
    iter_type iter; 
    }; 

    std::unique_ptr<Wrapper> ptr; 
    Impl(); 
}; 
template<typename K, typename T> 
Impl<K, T>::Impl() : ptr(new Impl<K, T>::Wrapper()) {} 

to znaczy, gdy potrzebna jest definicja Wrapper i utworzono instancję, Impl.

+0

Czy masz na myśli różnicę polegającą na tym, że konstruktor 'Wrapper' nie został skompilowany przed zaimplementowaniem ** klasy' Impl' ** w pełni implementowanej? Tak więc Wrapper tworzy sztucznie całą konstrukcję dwuetapowego procesu, gdy efektywnie kolejność kompilacji może być dokładnie taka sama? –

+0

@JoSo: Członkowie szablonów klas są tworzone instancjami, gdy są używane. 'Wrapper' jest członkiem szablonu klasy i jest używany tylko w definicji konstruktora. 'Typedef' tak naprawdę nie jest członkiem i zdefiniowanie szablonu klasy' Impl' jest instancją. Tak, tak, czyniąc go procesem dwustopniowym, unikaj problemu typu, który jednak nie został zdefiniowany. –

+0

Nie powinno być sposobu, aby powiedzieć kompilatorowi "tak, ten typ naprawdę istnieje!" ?. To znaczy, mogę też powiedzieć 'struct NewType; NewType * ptr; 'wewnątrz klasy bez definiowania NewType i działa! Czy jest to tylko słabość specyfikacji/implementacji, czy też istnieją głębsze przyczyny? –

Powiązane problemy