Подтвердить что ты не робот

С++ равная (==) перегрузка, ярлык или лучший способ сравнения всех атрибутов

Мне нужно перегрузить оператор == в С++ для класса со многими атрибутами.
Оператор должен возвращать true, тогда и только тогда, когда все атрибуты равны. Ярлык может быть полезен, если эти атрибуты меняются со временем, чтобы избежать ошибок.

Есть ли ярлык для сравнения каждого атрибута в классе?

4b9b3361

Ответ 1

Нет ярлыка. Вам нужно будет перечислить все.

Некоторые источники ошибок могут быть уменьшены путем введения функции-члена с именем tied(), например:

struct Foo {
    A a;
    B b;
    C c;
    ...

private:
    auto tied() const { return std::tie(a, b, c, ...); }
};

Чтобы ваш operator== мог просто использовать это:

bool operator==(Foo const& rhs) const { return tied() == rhs.tied(); }

Это позволяет вам только перечислять все ваши члены один раз. Но об этом. Вы все равно должны их перечислить (так что вы все равно можете его забыть).


Есть предложение (P0221R0), чтобы создать значение по умолчанию operator==, но я не знаю, будет ли оно принято,

Ответ 2

Начиная с С++ 11 с введением кортежей, мы также получили std::tie(). Это позволит использовать кортеж из множества переменных и вызвать функцию сравнения для всех из них. Вы можете использовать его как

struct Foo
{
    int a,b,c,d,e,f;
    bool operator==(const Foo& rhs) { return std::tie(a,b,c,d,e,f) == std::tie(rhs.a,rhs.b,rhs.c,rhs.d,rhs.e,rhs.f); }
};

Вам все равно нужно перечислить всех членов, которые вы хотите проверить, но это упростит. Вы также можете использовать это, чтобы сделать намного проще и легче сравнений.

Следует также отметить, что переменные проверяются в том порядке, в котором вы предоставляете их tie. Это важно для сравнений меньше или больше.

std::tie(a,b) < std::tie(rhs.a, rhs.b);

Не обязательно быть таким же, как

std::tie(b,a) < std::tie(rhs.b, rhs.a);

Ответ 3

На данный момент нет ярлыка, но есть планы добавить поддержку для него (P0221R0).

Bjarne Stroustrup недавно написал сообщение в блоге об этом: Немного фона для сравнения по умолчанию

В С++ 14 нет ничего лучше, чем перечисление всех членов и их сравнение, что является склонным к ошибкам. Процитировать Bjarne:

Аргумент killer для сравнения по умолчанию не является на самом деле удобством, а тем, что люди ошибаются в своих операторах равенства.

Ответ 4

Единственный способ сделать это, к сожалению, проверить все атрибуты. Хорошо, если вы объедините все свои чеки с помощью &&, он перестанет оценивать после первого ложного оператора. (оценка короткого замыкания)

Так, например, false && (4 == 4). Программа никогда не будет оценивать часть 4 == 4, поскольку все утверждения, объединенные в &&, должны быть true, чтобы получить true в качестве конечного результата. Это имеет смысл?

Ответ 5

Возможно, существует не соответствующее operator== решение. Вы можете сгенерировать связанный код из таблицы определений с помощью так называемого X-Macro. Таблица может выглядеть как

#define MEMBER_TBL                    \
/*type        ,name ,default*/        \
X(int         ,_(i) ,42     )         \
X(float       ,_(f) ,3.14   )         \
X(std::string ,  t  ,"Hello")         \

Материал _() необходим, чтобы избежать завершения , при генерации вызова std::tie(). Убедитесь, что последний элемент w.o. _(). Для генерации членов используется следующее:

struct Foo
{
#define _(x) x
#define X(type, name, default) type name{default};
    MEMBER_TBL
#undef X
#undef _
}

Это генерирует:

struct Foo
{
    int i{42}; float f{3.14}; std::string t{"Hello"};
}

Чтобы сгенерировать operator==, вы можете использовать:

bool operator==(Foo const& other) const {
        return  std::tie(
#define _(x) x,
#define X(type, name, default) this->name
            MEMBER_TBL
#undef X
        ) == std::tie(
#define X(type, name, default) other.name
            MEMBER_TBL
#undef X
#undef _
        );
    }

что приводит к

bool operator==(Foo const& other) const {
    return std::tie(
                     this->i, this->f, this->t
    ) == std::tie(
                  other.i, other.f, other.t
    );
}

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

Еще одно преимущество: вы можете просто добавить метод dump(), например

void print(void) const { 
    #define STR(x) #x
    #define _(x) x
    #define X(type, name, default)            \
            std::cout <<                      \
                STR(name) << ": " << name << " ";
            MEMBER_TBL
    #undef X
    #undef _
    #undef STR
            std::cout << std::endl;
        }

что приводит к

void print() const {
    std::cout << "i" << ": " << i << " "; std::cout << "f" << ": " << f << " "; std::cout << "t" << ": " << t << " ";
    std::cout << std::endl;
}

Каждая информация, относящаяся к членам, может быть добавлена ​​в таблицу в одном месте (одна точка информации) и извлечена в другом месте.

Рабочий Демо.