Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: unique_ptr complains about incomplete type, but not when I wrap it

Tags:

c++

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;
};
like image 434
Jo So Avatar asked Oct 22 '22 02:10

Jo So


1 Answers

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.

like image 162
Dietmar Kühl Avatar answered Oct 30 '22 02:10

Dietmar Kühl