Cannot initialize constant base member value

Started by
8 comments, last by Servant of the Lord 9 years, 1 month ago

Okay, I've literally struggled with this for hours, because I had a base class that had a value


class base
{
public:
const __int32 ID;
};

the ID would be filled with a value that designates what kind of entity it is, and the program wouldn't work unless I also defined a const __int32 ID in my child classes as well. This led to ambiguous use of ID which caused me many hours of frustration. And now I simply can't figure out how to do it; if I remove the second ID from the child class it won't allow me to use the constant in my base class under any circumstance. I've looked online and nobody else seems to have this issue. I tried doing fancy things with overloading constructors and whatever, but the compiler will not let this go. I don't know what rule is keeping me from assigning an ID value into my child class when creating a new object of this type. I wouldn't know what this issue is called. I have the error code and that didn't help me. (For instance, C2614: 'Block' : illegal member initialization: 'ID' is not a base or member)

I'm really tired and out of it and I essentially spent an all nighter on a probably really stupid thing. Does anyone know what problem I'm having so I can move on with my life?

Advertisement

Constant members can only be initialized in the constructor's initializer list, but there's no constructor in your base class to initialize it. Add a constructor that takes an ID so that the base class can initialize its member and call that base constructor from the derived classes.

class base
{
public:
base(__int32 ID) : ID(ID) {}
const __int32 ID;
};

Okay, sorry, again, I'm tired, so I'm skimming details. I do have that.


class Entity
{
static unsigned short Count;


public:
Entity() : ID (eIDError) {}
Entity(EntityIDs id) : ID (id) {}
const __int32 ID;
};


class Block : public Entity
{
public:
Block() : ID (eIDError) {}
Block(EntityIDs id) : ID (id) {}
};

In the Block class it gives me the same aforementioned error:


error C2614: 'Block' : illegal member initialization: 'ID' is not a base or member 

Unless I redefine ID in the child class, which again, brings up issues over the ambiguous ID.

You need to call the base-class constructor, not attempt to initialise the base-class variable directly. Something like:

Block() : Entity(eIDError) {}

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

You need to initialize the base class members from the base class, you cannot do it from the derived classes.

class Block : public Entity
{
public:
Block() : Entity(eIDError) {}
Block(EntityIDs id) : Entity(id) {}
};

Oh gosh. Thank you so very much. Everything's working now.

These reason for this is that because 'ID' is constant, it must be initialized to its value. It can't be assigned later.

Because the base constructor always runs before the derived constructor, by the time you get to the derived constructor's initializer list, ID has already been created, and your derived constructor is trying to re-initialize (i.e. assign to) an already constructed value. Even if you didn't explicitly initialize it - it'll just be holding gibberish - but the int's "constructor" (so-to-speak, but not really a constructor) has already ended before the derived class's constructor begins.

the ID would be filled with a value that designates what kind of entity it is...
...I don't know what rule is keeping me from assigning an ID value into my child class when creating a new object of this type.


If it's being used to differentiate types of classes (i.e what kind of derived class it is), shouldn't the ID be the same for every instance of that derived class?

Are you trying to ID class instances or the type of class itself? Do you want every 'Block' entity to have the same ID or a unique one?

If you want all instances of the same Derived class to share the same ID, you can do this:


enum class Type { Block, Soldier, Wizard };
 
class Base
{
    public:
    virtual Type GetType() const = 0; //By being pure-virtual, it prevents Base instances from being created on their own.
};
 
class Block : public Base
{
    public:
    Type GetType() const override { return Type::Block; }
}

This way, when you call:


Base *basePointer = new Block;
basePointer->GetType();

...it'll return Type::Block.

I'm using the EntityIDs enum to differentiate between what kinds of objects are colliding during each Collide() function. So my IDs consist of things like Block, Player, Enemy, etc. It's what I'd figure I'd use. Since I don't intend to have any of the types change it's also a constant that is accessible publicly so I don't have to have a GetType function. Just other->ID. That's still fine, right? Though I guess it is kind of a hassle with stuff like this.

I've seen games that have entities that each have unique IDs. Would I come up with some method for generating one and then initialize them with that unique ID in a similar fashion?

There are many other options for that.

Probably the easiest that also avoids a virtual function is to make the variable private, accept the value in a constructor, and write a non-virtual inline accessor function that returns the value.

I'm using the EntityIDs enum to differentiate between what kinds of objects are colliding during each Collide() function. So my IDs consist of things like Block, Player, Enemy, etc. It's what I'd figure I'd use. Since I don't intend to have any of the types change it's also a constant that is accessible publicly so I don't have to have a GetType function. Just other->ID. That's still fine, right? Though I guess it is kind of a hassle with stuff like this.

Yep, that's still fine.

EntityIDs implies (at least to me) that it's a unique identifier per entity instance. EntityTypeID would perhaps be a better name, since it implies each type of entity (Block, Enemy, etc...) has a unique ID. smile.png

I've seen games that have entities that each have unique IDs. Would I come up with some method for generating one and then initialize them with that unique ID in a similar fashion?


Having a unique ID per-entity is useful for other purposes, but isn't what you want here for implementing your collision logic. If you want both (for those other uses), you could do it like:


//__int32 is implementation-specific. int32_t and uint32_t are standardized,
//but you need to #include <cinttypes> for it.
#include <cinttypes>
 
enum class EntityType { Player, Block, Enemy };
 
class Entity
{
   public:
   const uint32_t ID; //Unique per entity.
   const EntityType Type; //Unique per type.

   public:
   Entity(uint32_t id, EntityType entityType) : ID(id), Type(entityType) { }

   protected:
   static uint32_t generateNextID() { return ++nextEntityID; }
   
   private:
   static uint32_t nextEntityID = 0;
};
 
class Block
{
   public:
   Block() : Entity(Entity::generateNextID(), EntityType::Block) { }
};

<opinion>

...but personally, I don't think entities should know their own ID. Usually, the ID is only used outside of the entity class to find the right entity, so I just do std::unordered_map<EntityID, Entity>, where the map is allowed to do its job of mapping the IDs to the entities.
And if performance becomes an issue during iteration, due to thousands of entities at once, I'd use a vector of entities with a unordered_map<EntityID, VectorIndex> backing it up.
</opinion>

But the simplest way starting out would be to keep the ID in the entity, and keep the entities in a vector, and search the vector for the correct entity when needed.

Important: Don't forget that when using polymorphism (i.e. treating a Derived as if it's a Base), you must refer to your instances by pointers! You can't do Base myBase = Derived, or it crams the bits of the Derived into a Base variable, parts of it get lost, and bad things result. wacko.png

This topic is closed to new replies.

Advertisement