Reputation: 233
Ok, I am working with g++ 4.8.2 and have the following (somewhat long) code that gets an error message about an incomplete type. I have reduced the code to a smaller chunk for inclusion here and can be compiled directly:
#include <cstdlib>
struct S
{
void method(){}
};
template<size_t sz, size_t tot>
class genpool
{
};
template <class T>
class mempool
{
private:
genpool<sizeof(T), 10*sizeof(T)> p;
};
template <class obj, class mthd>
class functor
{
private:
static mempool<functor<obj, mthd> > pool;
};
template <class obj, class mthd>
mempool<functor<obj, mthd> > functor<obj, mthd>::pool;
int main()
{
typedef void (S::*m)();
typedef functor<S, m> fctr;
fctr f;
}
The compiler error message is:
g++ jj.C
jj.C: In instantiation of ‘class mempool<functor<S, void (S::*)()> >’:
jj.C:30:30: required from ‘class functor<S, void (S::*)()>’
jj.C:37:8: required from here
jj.C:18:17: error: invalid application of ‘sizeof’ to incomplete type ‘functor<S, void (S::*)()>’
genpool<sizeof(T), 10*sizeof(T)> p;
^
Compilation exited abnormally with code 1 at Thu Apr 9 18:50:06
Obviously, the template functor is defined above and all the arguments to functor have been explicitly defined. This seems to imply to me that the sizeof function should be well defined. Is there something that I am missing here?
--Ron
Upvotes: 8
Views: 180
Reputation: 70412
The problem is that the compiler is attempting to instantiate mempool<>
before it instantiates functor<>
. This is because the compiler feels it needs to be able to define the static member functor<>::pool
first before functor<>
itself is considered fully defined.
A workaround is to return a mempool<> &
from a static member function.
template <class obj, class mthd>
class functor
{
private:
static mempool<functor> & get_pool () {
static mempool<functor> pool;
return pool;
}
};
//template <class obj, class mthd>
//mempool<functor<obj, mthd> > functor<obj, mthd>::pool;
This works because the reference means it is okay for mempool<>
to remain incomplete until after functor<>
is instantiated. (Actually, a method of a template is not instantiated unless there is code that actually calls it.) When the static method is called, functor<>
itself is complete, so the static object within functor<>::get_pool
can be properly instantiated.
As a side note, it is acceptable to pass an incomplete type as an argument to a template, but the template has restrictions on what it can actually do with an incomplete type. Everything is fine if the template only requires a reference or pointer to the type for its instantiation.
Upvotes: 6
Reputation: 2865
At the time you declare pool
inside of functor
, you are still defining the functor
class, so the type functor
is still incomplete.
This is similar to forward declarations:
class functor;
functor func; <<-incomplete here
functor *ptr; <<-pointer is fine
class functor
{
functor func; <<-incomplete again
};
functor func; <<-now complete definition, ok
Upvotes: 1
Reputation: 2822
Your definition of functor
is recursive. It requires the compiler to know the size of the type of functor
while determining the functor
type. You can generate exactly the same problem with this code:
template <class A>
class B {
public:
static const int szA = sizeof(A);
};
template <class A>
class C {
public:
static B<C<A> > b;
};
int main() {
C<int> c;
}
Depending on what your application is, you should be able to do what you want using type traits.
Upvotes: 1
Reputation: 20838
I don't think you can do it, because you have a recursive definition. For example, you can't do this:
#include <cstdlib>
class B;
class A
{
B b;
};
class B
{
A a;
};
int main()
{
A x;
}
The only way out is to make one of the members a pointer instead of an instance.
Upvotes: 0