There are already a good number of questions about unique_ptr and incomplete type on SO, but none can give me a concept in understanding why the following does not work:
// 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; }
while the following does:
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; }
I don't see where the technical difference is: If std::pair<...>::second
(that is, Impl<K,T>
) was incomplete to Impl
in the first example, it should be incomplete to Wrapper
in the second one as well. Also,
when it suffices to wrap the unique_ptr
in a struct, why is there a restriction for the first case?
UPDATE:
After Dietmar Kühl's answer, I think the problem can be reduced to the following:
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;
};
The problem in the first case the incomplete type is used with the std::unordered_map<K, Impl<K, T>
: To determine what iterator
is, parts of std::unordered_map<K, Impl<K, T>
need to be instantiated at a time when Impl
is merely declared. The std::unique_ptr<...>
has not thing to do with error. You can remove the use of iter_type
as the typedef
needs to verify that it is a type.
On the other hand, when wrapping the use of the iterator type into Wrapper
, this nested type isn't used prior to the constructor implementation. Of course, inline defined functions behave as if the class was just fully defined and they are implemented just outside the class definition, i.e., the above code is equivalent to
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()) {}
That is, when the definition of Wrapper
is needed and instantiated, Impl
is defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With