Не удается найти последний вставленный ключ в std::map

Моя проблема в том, что какой бы самый последний элемент я ни вставил в std::map, я не смогу его найти.

У меня есть следующая карта, которая принимает цвет в качестве ключа и кодирует его в некоторый тип объекта (перечисление).

class ColorEncoder {
private:
    std::map<Color, Object::Type> validObjects;

public:
    ColorEncoder() {
        load(Color(76, 177, 34), Object::Player);
        load(Color(36, 28, 237), Object::Block);
     // load(Color(0, 111, 222), Object::PleaseDont); // uncomment this line and everything will work correctly,
                                                      // but the map will have one garbage value
    }

    void load(Color color, Object::Type type) {
        validObjects.insert(std::make_pair(color, type));
    }

    auto& getValidObjects() {
        return validObjects;
    }
};

У меня также есть array цветов. Цель состоит в том, чтобы проверить, действительно ли каждый элемент массива существует на карте.

Следовательно, я перебираю массив и каждый раз проверяю, существует ли текущий элемент массива в качестве ключа на карте:

class BMP_Analyzer {
public:
    // assume data size is 12
    BMP_Analyzer(std::unique_ptr<Color[]>& data, std::map<Color, Object::Type>& validObjects) {
        for (int i = 0; i < 12; i++) {
            if (validObjects.find(data[i]) == validObjects.end()) {
                std::cout << "Not found in the map!\t";
                std::cout << "Blue: " << (uint16_t) data[i].blue << " " << " Green: "
                          << (uint16_t) data[i].green << " Red: " << (uint16_t) data[i].red
                          << " is not mapped!" << std::endl;
            }
        }
    }
};

Примерный вывод:

Not found in the map!   Blue: 36  Green: 28 Red: 237 is not mapped!
Not found in the map!   Blue: 36  Green: 28 Red: 237 is not mapped!

Однако, если я раскомментирую это:

// load(Color(0, 111, 222), Object::PleaseDont);

Тогда он правильно обнаружит ранее не найденный выше цвет: (36, 28, 237).

Мне это кажется неправильным или что-то в этом роде, но, честно говоря, я понятия не имею, где потенциальная ошибка.

Цвет определяется следующим образом, с перегруженным operator<, поэтому он может работать как ключ с std::map.

struct Color {
    uint8_t blue;
    uint8_t green;
    uint8_t red;

    Color() = default;

    Color(uint8_t blue, uint8_t green, uint8_t red)
        :blue{blue},
         green{green},
         red{red}
    {
    }

    bool operator<(const Color& other) const {
        return blue != other.blue || green != other.green || red != other.red;
    }
}

Любые подсказки, где может лежать проблема, очень приветствуются, спасибо.


person weno    schedule 26.12.2019    source источник
comment
Ваш оператор сравнения не определяет строгий, слабый порядок. Таким образом, использование вами типа в std::map приводит к неопределенному поведению.   -  person Dietmar Kühl    schedule 26.12.2019
comment
@DietmarKühl: Спасибо!   -  person weno    schedule 26.12.2019


Ответы (1)


Сравнение ключей в st::map должно быть строгим, слабым порядком, т. е. должны выполняться следующие правила:

  1. (a < a) == false
  2. (a < b) == true && (b < c) == true подразумевает (a < c) == true
  3. (a < b) == true подразумевает (b < a) == false
  4. (a < b) == false && (b < a) == false) && (b < c) == false && (c < b) == false) подразумевает (a < c) && (c < a) == false

Самый простой способ добиться этого для структуры — использовать сравнение std::tuples:

bool operator< (Color const& c0, Color const& c1) {
    return std::tie(c0.blue, c0.green, c0.red) < std::tie(c1.blue, c1.green, c1.red);
}

Этот оператор сравнения на самом деле определяет более сильный порядок: общий порядок.

person Dietmar Kühl    schedule 26.12.2019