Search code examples
c++sfml2d-games

Can't delete element from vector, attempting to reference deleted function


I am making my "version" of Space Invaders in C++ using SFML library but I have a problem when I try to delete an invader. I have this error in my code:

Enemy &Enemy::operator =(const Enemy &)': attempting to reference a deleted function

I tried to check other solutions that were recommended in this forum but I either didn't understand them or it was a different case.

EnemyControler.cpp

EnemyControler::EnemyControler()
{
    Enemy::Type types[] = {
        Enemy::Type::Squid, Enemy::Type::Crab, Enemy::Type::Crab,
        Enemy::Type::Octopus, Enemy::Type::Octopus
    };


    for (int y = 0; y < 5; y++) { // Add enemies to the vector
        for (int x = 0; x < 11; x++){
            float enemyX = x * 40 + (gapBetweenEnemies * x * 3) + Enemy::enemyWidth; // Make horizontal gap between them
            float enemyY = y * 40 + (gapBetweenEnemies * y) + Enemy::enemyHeight; // Make vertical gap between them
            enemies.emplace_back(sf::Vector2f{ enemyX, enemyY }, types[y]); // Add them to the vector
        }
    }
}
void EnemyControler::destroyEnemy()
{
    for (auto iterator = begin(enemies); iterator != end(enemies);) {
        auto& enemy = *iterator;
        if (enemy.isAlive()) {
            iterator++;
        }
        else {
            iterator = enemies.erase(iterator);
        }
    }
}

Problem is in destroyEnemy function. Specifically in iterator = enemies.erase(iterator);

EnemyControler.h

class EnemyControler
{
public:
    EnemyControler();

    void destroyEnemy();
private:
    const int gapBetweenEnemies = 10;

    std::vector<Enemy> enemies;
};

Enemy.cpp

Enemy::Enemy(sf::Vector2f startingPosition, Type type) : 
Collidable(enemyWidth, enemyHeight), newPosition(startingPosition), enemyType(type), startingPosition(startingPosition)
{
}

Enemy.h

class Enemy : public Collidable
{
public:
    enum class Type // enum for different looking enemies
    {
        Crab, Octopus, Squid
    };
    Enemy(sf::Vector2f startingPosition, Type type);

    constexpr static float enemyWidth = 30.0f;
    constexpr static float enemyHeight = 30.0f;

private:
    sf::Vector2f newPosition;
    Type enemyType;

    const sf::Vector2f startingPosition;
};

Collidable.cpp

Collidable::Collidable(float width, float height) :
 spriteSize(width, height)
{
}

Collidable.h

class Collidable
{
public:
    Collidable(float width, float height);
private:
    sf::Vector2f spriteSize;
};

If there isn't an easy way of fixing this problem or this problem could be fixed only by rewriting all code maybe you could suggest another way of deleting invader from the vector.


Solution

  • If a class has a const member variable, then the copy assignment operator for that class operator= is default-deleted by the compiler.

    From https://en.cppreference.com/w/cpp/language/copy_assignment :

    A defaulted copy assignment operator for class T is defined as deleted if any of the following is true:
    - T has a non-static data member of non-class type (or array thereof) that is const;

    This is because the compiler can't guess what it means to copy assign object A into object B, if objects of their class contain a const member variable. Should the compiler just ignore that particular member variable? Should the compiler guess that you mean it to be const, except when copy assigning, then it's okay just this once - I'll look the other way? The compiler has no idea what you would prefer, so it just deletes it.

    One option is to explicitly define a copy assignment operator for your class.

    However, startingPosition is already declared private, so there's little chance of anything outside of the class inadvertently changing it. I recommend just removing the const specifier.

    Why does the copy assignment operator matter? I'm trying to delete things, not copy assign them

    When an element is erased from a vector, all elements in the vector "above" the erased element need to be moved down to fill the gap. This happens via the copy assignment operator.

    From https://en.cppreference.com/w/cpp/container/vector/erase :

    Complexity
    Linear: the number of calls to the destructor of T is the same as the number of elements erased, the assignment operator of T is called the number of times equal to the number of elements in the vector after the erased elements