Здравствуйте, ·, Вы писали:
·>Если использовать наследование, например как "struct Id : std::vector<int> {}", то сравниваться будут, а не должны.
Справедливое замечание. Но, опять же, масштаб трагедии здесь ровно такой же, как и при использовании целочисленных айдишников. А их использовали, используют и будут использовать.
Мне и самому вариант решения с наследованием не очень нравится. Я вообще не люблю наследование, стараюсь его избегать. Но даже в этом варианте решения возможно достичь приемлемой типовой безопасности, накидав в тело класса несколько дополнительных определений. А чтоб не делать это для каждого типа в отдельности, можно использовать шаблон:
http://coliru.stacked-crooked.com/a/0e65b861f54e6293
#include <iostream>
#include <vector>
template <typename Category>
struct MultiDimensionalId : std::vector<int>
{
using vector::vector; // Открываем все конструкоры vector для использования в этом классе
bool operator == (const MultiDimensionalId&) const = default;
auto operator <=> (const MultiDimensionalId&) const = default;
bool operator == (const auto&) const = delete;
auto operator <=> (const auto&) const = delete;
};
using ElementId = MultiDimensionalId<class ElementCategory>;
using LogicalId = MultiDimensionalId<class LogicalCategory>;
int main()
{
std::cout << (ElementId{1, 2, 3} == ElementId{1, 2, 3}) << std::endl;
std::cout << (ElementId{1, 20, 3} >= ElementId{1, 2, 3}) << std::endl;
std::cout << (ElementId{1, 2, 3} < ElementId{1, 20, 3}) << std::endl;
// std::cout << (ElementId{1, 2, 3} == LogicalId{1, 2, 3}) << std::endl; // error: use of deleted function
//std::cout << (ElementId{1, 2, 3} >= LogicalId{1, 2, 3}) << std::endl; // error: use of deleted function
}
Повторюсь, я не считаю это лучшим решением, и не говорю, что делать нужно только так. Как по мне, идиома строгих типов, в том или ином варианте исполнения, здесь была бы в самый раз.