tags:

views:

109

answers:

2

Recently I am having many problem with typedef and incomplete type when I changed certain containers, allocators in my code.

What I had previously

struct foo;//incomplete type.
typedef std::vector<foo> all_foos;
typedef all_foos::reference foo_ref;

Though not completely not sure whether the above lines are legal, but this worked on every implementation I used. When I thought that I can do the job with std::tr1::array, changed the above two lines with

typedef std::tr1::array<foo,5> all_foos;
typedef all_foos::reference foo_ref;

Here everything breaks, as the compiler tries to instantiate array and fails as foo is incomplete type. What all I needed is a reference to foo, and not much interested on 'other parts' of the array. foo will definitely be completely available when I create such an array.

The same is problem when typedef std::allocator<foo>::pointer foo_ptr got replaced by typedef stack_alloc<foo,10>::pointer foo_ptr. where a stack_alloc implementation is like

template<typename T,unsigned N>
struct stack_alloc
{
  typedef T* pointer;
  typedef std::tr1::aligned_storage<sizeof(T)*N, std::tr1::alignment_of<T>::value> buffer;
};

Presuming that, value_type, pointer, reference, iterator etc does not depend on the completeness of T, and knowing that the class can not be instantiate without complete type, how such typedef can be made in generic way independent of specific container or allocator?

NOTE:

  • Just for completeness, in 'real' code I use a small local memory with vector rather than replacing it with std::array, though the problem remains same.
  • stack_alloc code is far from complete, and only shows the part of the problem.
  • I know that array, sizeof etc needs complete type available. But I am NOT creating object of type all_foos with incomplete foo.
  • My assertion is that pointer,reference etc should not depend on completeness of a type. Otherwise construct like struct foo{ foo_ptr p;}; can not be defined. Though probably foo_ref can not be anything other than foo&, but foo_ptr can be. Surprisingly GCC implementation doesn't have nested pointer type for tr1::array.
  • Know mostly what can not be done, and interested to know what can be done in this situation. So expecting a good design as a solution.
+3  A: 

A type must be complete to be used in a standard container, or the behavior is undefined (§17​.4.3.6/2). So the only standard solution is to not make that typedef until the class is defined.

I don't get what the intermediate container is for:

struct foo;//incomplete type.
typedef foo& foo_ref;

In any case, you'll just have to have the complete type defined first, really. To get a typedef defined in a class, that class must be instantiated, which means the entire thing must be able to use T as desired.

For example, stack_alloc must have T be a complete type (for sizeof(T) to work), otherwise the class cannot be instantiated. If the class can't be instantiated, you cannot get the typedef out of it. Ergo, you'll never get the typedef out of it if T is incomplete.

GMan
The container is where foo will get stored. struct foo; typedef std::vector<foo, alloc<foo> > all_foos; typedef all_foos::pointer foo_ptr; struct bar { foo_ptr p;}; struct foo { std::vector<bar> bars; }; all_foos the_instance; does it makes sense? If I define struct bar { foo* p; }; It may NOT work as the alloc may have different pointer than foo*.typedef foo is ok as no other way reference can be defined.
abir
@abir: I can't really think of an example where the pointer type would change. In any case, if you really want to get those typedefs', you'll just have to include the full definition of `foo`. No other way.
GMan
A: 

Compiller doesn't know the size of incomplete type, therefore it can not instantiate it nor allocate some memory for it. Having a pointer to object (like typedef std::tr1::array<foo*, 5> all_foos; ) instead of instance of the object itself solves this issue.

Dark