5

I have

template<typename T>
class queue
{
private:
    struct node
    {
        T data;
        std::unique_ptr<node> next; //compile error on incomplete type

        node(T&& data_):
            data(std::move(data_))
        {}
    };

    std::unique_ptr<node> head;
    node* tail;

public:
        queue():
            tail(nullptr)
        {}

I get compile error on marked line in VS10. Shouldn't I be allowed to use an incomplete type in this case (instantiate the template - the constructor - here for int as an example) ? Is there a work-around ?

EDIT

singlethreadedqueue.h(62): error C2079: 'queue<T>::node::next' uses undefined class 'std::unique_ptr<_Ty>'
1>          with
1>          [
1>              T=MyClass
1>          ]
1>          and
1>          [
1>              _Ty=queue<MyClass>::node
1>          ]
1>          c:\program files\microsoft visual studio 10.0\vc\include\memory(2161) : see reference to class template instantiation 'queue<T>::node' being compiled
1>          with
1>          [
1>              T=MyClass
1>          ]
1>          c:\program files\microsoft visual studio 10.0\vc\include\memory(2195) : see reference to class template instantiation 'std::_Unique_ptr_base<_Ty,_Dx,_Empty_deleter>' being compiled
1>          with
1>          [
1>              _Ty=queue<MyClass>::node,
1>              _Dx=std::default_delete<queue<MyClass>::node>,
1>              _Empty_deleter=true
1>          ]
1>         singlethreadedqueue.h(69) : see reference to class template instantiation 'std::unique_ptr<_Ty>' being compiled
1>          with
1>          [
1>              _Ty=queue<MyClass>::node
1>          ]
1> : see reference to class template instantiation 'queue<T>' being compiled
1>          with
1>          [
1>              T=MyClass
1>          ]
Ghita
  • 4,465
  • 4
  • 42
  • 69

3 Answers3

5

The requirement of complete type at the template instantiation point is due to std::default_delete, so probably you could work-around it by providing a custom deleter.

struct node;         // node_delete need to know 'node' is a type.
struct node_deleter 
{                    // std::unique_ptr needs 'node_deleter' to be complete.
    void operator()(node* ptr);  // forward-reference to avoid needing
};                               //  'delete ptr' before the node is complete.

struct node
{
    std::unique_ptr<node, node_deleter> next;
};

void node_deleter::operator()(node* ptr)
{
     delete ptr;
}

Mind you, I have not tested it in MSVC, and probably you should try to upgrade to VC 11, or file a bug to Microsoft if even VC 11 cannot compile your original code.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
3

Your code looks well-formed. In a constructor body and destructor body, a class is considered to be a complete type.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • compiling with gcc 4.5.1 (C++0x) is OK indeed... wonder if there is a work-around using VS10 now – Ghita May 02 '12 at 19:56
  • compiling with gcc 4.3.4 though (using http://ideone.com) I am in same problem as with VS10 – Ghita May 02 '12 at 20:00
  • 1
    @Ghita : Ideone's GCC 4.3.x doesn't enable C++11 support, so it doesn't have `std::unique_ptr<>`, which is quite a _different_ problem than with VC++2010. – ildjarn May 02 '12 at 20:06
  • @ildjarn it is possible. Just compiled that online – Ghita May 02 '12 at 20:08
0

If you use shared_ptr instead of unique_ptr, then this compiles (on VS2010). I'm guessing that the way it's implemented allows partial classes (forward declaration, etc) in the case of shared_ptr, but not for unique_ptr. The doc for boost::shared_ptr definitely says it allows this, but I don't know for VS2010 if it's "said" why they're doing this behavior for one and not the other.

I'd say test it and see if it meets your needs with shared_ptr. It's not what you want, but if it works, and the speed difference isn't a lot, then it could be OK.

Kevin Anderson
  • 6,850
  • 4
  • 32
  • 54
  • I'd rather not use shared_ptr instead of a uniquely owned pointer. – Ghita May 02 '12 at 20:26
  • I agree. For what you're doing, `unique_ptr` seems the appropriate choice, but sometimes you have to use what works, even if it isn't what you originally wanted to use. I was just posting as a "well, this would make your code work" alternative, even if it's not the explanation you wanted. – Kevin Anderson May 02 '12 at 20:36