4

Stripped down to the bare minimum, here's the code I'm trying to compile:

template<class T>
class B
{
  protected:

    std::vector<typename T::I> v;

  public:

    template<class... Args>
    void add(Args... args )
    {
      this->v.emplace_back(std::forward<Args>(args)...);
    }

    typename T::I get(int i)
    {
      return this->v[i];
    }
};

class D : public B<D>
{
  public:

    typedef std::string I;
};

If I instantiate D and try to compile this in g++, it complains:

error: invalid use of incomplete type ‘class D’

std::vector<typename T::I> v;

and adds a note,

note: forward declaration of ‘class D’

class D : public B<D>

If I try clang++ instead, I get a different error:

error: no type named 'I' in 'D'

std::vector<typename T::I> v;

I'm sure I'm just doing something silly here but I can't seem to figure it out.

max66
  • 65,235
  • 10
  • 71
  • 111
Daniel McLaury
  • 4,047
  • 1
  • 15
  • 37
  • At the point `std::vector` `T` is incomplete. The compiler is right. – πάντα ῥεῖ Jun 20 '19 at 20:13
  • I dimly remember I used this style a few years ago (maybe 10 years). Back then, it worked for me (on the then current MSVC compiler). Recently I tried something like that, as well and I ran into some problems. I attributed that to me being rusty :) – BitTickler Jun 20 '19 at 20:13
  • 1
    MSVC is notorious for letting weird things fly with templates. – Radosław Cybulski Jun 20 '19 at 20:13
  • 1
    Possible duplicate: [C++ static polymorphism (CRTP) and using typedefs from derived classes](https://stackoverflow.com/q/6006614/27678) – AndyG Jun 20 '19 at 20:17

1 Answers1

4

The problem is just that when you write

class D : public B<D>

you pass the class D as template parameter of B when D is still incomplete.

So the compiler can't see D::I because isn't defined at this point.

g++ explicitly say this: "error: invalid use of incomplete type ‘class D’".

clang++ dosn't say this explicitly but doesn't find I for this reason.

-- EDIT --

The OP asks

How do you do what I want to do, then?

The only solution that I see (not a great solution, unfortunately) is define what you need in B in another class/struct, say C

struct C
 { using I = std::string; };

and inherit D from C and B<C>

class D : public C, public B<C>
 { };

This way C is fully defined, when you use it as template parameter for B, and also inherited from D.

max66
  • 65,235
  • 10
  • 71
  • 111