0

I've been attempting a lot of template meta programming lately, particularly using CRTP, and have come across the titular error. Specifically error C2352 'MeshComponent::InternalSetEntity': illegal call of non-static member function.

A Minimal, Complete, and Verifiable snippit of my code is as such:

Component.h

class Entity //Forward declaration

template<class T, EventType... events>
class Component {
private:
    short entityID;
public:
    void SetEntity(Entity& _entity) { entityID = _entity.GetId(); T::InternalSetEntity(_entity); }
protected:  
    void InternalSetEntity(Entity& _entity) {}
};

MeshComponent.h

#include "Component.h"
class MeshComponent : public Component<MeshComponent> {
    friend class Component<MeshComponent>;
protected:
    void InternalSetEntity(Entity& _entity);
};

MeshComponent.cpp

#include "MeshComponent.h"
void MeshComponent::InternalSetEntity(Entity& _entity) {
    //Nothing yet
}

Entity.h

class Entity {
private:
    short id;
public:
    short GetId() {return id;}
};

I am not declaring any static functions, nor do I want to. In fact I require these to be member functions as they will operate on instance specific data.

If someone knows why this error is occurring and a possible solution to the problem I would greatly appreciate it. Thanks in advance.

Quentin
  • 62,093
  • 7
  • 131
  • 191
Ryoku
  • 397
  • 2
  • 16

1 Answers1

0

You know and ensure that MeshComponent inherits from Component<MeshComponent>, but the compiler doesn't know that: as far as it knows in the definition of Component, Component<T> and T are unrelated. You need to perform the downcast explicitly:

static_cast<T*>(this)->InternalSetEntity(_entity);
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Wow I would have never thought about that but that makes total sense as to why its complaining about non-static functions. So to complete CRTP we need to specifically tell it to use its own instance. There's something oddly beautiful about having to point out what seems obvious to us when using templates. Just to make sure I have this last part right, we have to downcast it to a pointer because "this" is a pointer. But "this" is a const pointer by the looks of it so are we able to downcast it to a reference instead? – Ryoku Feb 23 '18 at 10:38
  • @Ryoku it being `const` has no bearing on that, you can indeed do `static_cast(*this)`. Which I typically factor out into a pair of (`const` and non-`const`) member functions. – Quentin Feb 23 '18 at 10:42
  • Awesome. What if I wanted to extend the parent construction method by tacking on the child's at the end? Kind of like `Component(short id) { entityID = id; T(id) }` Would that suffice or would I have to downcast `T` again? – Ryoku Feb 23 '18 at 10:47
  • @Ryoku no, that has no way to work. But you do not need it, since the derived constructor already calls the base one before doing anything else. Note that you *cannot* downcast in the constructor (or destructor) since the derived object doesn't exist yet (or anymore). – Quentin Feb 23 '18 at 11:40
  • Okay so the base constructor is called anyways, I didn't think it would since the base was a template, that's great to know. Thanks so much – Ryoku Feb 23 '18 at 11:42
  • @Ryoku a class template, once you provide suitable template arguments, produces a class that acts as any other apart from a few syntactic hints (`template` and `typename`). It's useful to always keep in mind that distinction between the template and its instantiation. – Quentin Feb 23 '18 at 11:45
  • Awesome tip. Still makes the whole constructor thing a little confusing though since technically Component does not exist so I am surprised that `Component()` would be called before `MeshComponent()` as if it was doing regular inheritance.... wait I just got it, there is a Component class created right before MeshComponent as part of the CRTP. So two classes are created for every "template inheritance" done this way – Ryoku Feb 23 '18 at 11:52
  • @Ryoku `Component` has to be created *somehow*, doesn't it? ;) -- The compiler [generates a default constructor](https://stackoverflow.com/questions/563221/is-there-an-implicit-default-constructor-in-c) if you don't provide one. – Quentin Feb 23 '18 at 11:55