Ronald Van Iwaarden
Ronald Van Iwaarden

Reputation: 233

Sizeof failing in template even though types are all defined

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

Answers (4)

jxh
jxh

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

fukanchik
fukanchik

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

Mokosha
Mokosha

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

Mark Lakata
Mark Lakata

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

Related Questions