Я реализовал operator<
для определенного объекта.
Логически, если !(a < b)
и !(b < a)
означает a == b
.
Это выводится автоматически? Могу ли я использовать ==
, если я использую только <
?
Я реализовал operator<
для определенного объекта.
Логически, если !(a < b)
и !(b < a)
означает a == b
.
Это выводится автоматически? Могу ли я использовать ==
, если я использую только <
?
С++ не может вывести это автоматически по нескольким причинам:
operator<
, поэтому тип необязательно может определять operator<
.
operator==
не может быть автоматически определен в терминах operator<
operator<
не требуется сравнивать свои аргументы. Программист может определять операторы для своих типов, чтобы сделать почти что угодно для своих аргументов.
!(a < b) && !(b < a)
, эквивалентное a == b
, может не обязательно быть истинным, если предположить, что эти операторы определены.Если вам нужна функция operator==
для ваших типов, просто определите ее самостоятельно. Это не так сложно:)
// For comparing, something like this is used
bool operator==(const MyType& lhs, const MyType& rhs)
{
// compare (or do other things!) however you want
}
// ... though it not the only thing you can do
// - The return type can be customised
// - ... as can both of the arguments
const MyType& operator==(int* lhs, const MyType* const rhs)
{
return lhs;
}
Он не может вывести ==
из <
, потому что не все типы упорядочены, например std::complex
. Является ли 2 + 3i > 1 + 4i
или нет?
Более того, даже в типах, которые обычно упорядочены, вы все равно не можете вывести равенство из >
или <
, например IEEE-754 NaN
double n = std::numeric_limits<double>::quiet_NaN();
std::cout << "NaN == NaN: " << (n == n) << '\n';
std::cout << "NaN < NaN: " << (n < n) << '\n';
std::cout << "NaN > NaN: " << (n > n) << '\n';
std::cout << "NaN != NaN: " << (n != n) << '\n';
Все они вернут false, кроме последнего
Нет. Этот метод хорошо работает на числовых объектах, который называется полностью заказанным. Для всех видов set/class никто не может гарантировать это отношение. Даже никто не может гарантировать, что operator <
что-то сравнит.
So ==
- это не что иное, как ==
. Вы можете реализовать ==
с помощью <
, но это не работает для всех, и стандарты С++ не сделают этого для вас.
Логически, если! (a < b) и! (b < a), это означает a == b. Вывод С++ это автоматически? Могу ли я использовать ==, если бы я только реализовал
Чтобы сформулировать то, что другие заявили в математических терминах: предположив, что у вас есть operator <
, который возвращает bool
и определяет строгий слабый порядок, и вы реализуете operator ==
как возвращающий !(a < b) && !(b < a)
, то этот оператор определяет эквивалентность, соответствующая данному строгому слабому порядку. Однако С++ не требует, чтобы operator <
определял строгий слабый порядок, а не operator ==
, чтобы определить отношение эквивалентности (хотя многие стандартные алгоритмы, такие как sort
, могут неявно использовать эти операторы и требуют строгого отношения rsp..
Если вы хотите определить все другие реляционные операторы, основанные на строгом слабом порядке operator <
, которые соответствуют вашему стандарту operator <
, Boost.Operators могут сэкономить вам некоторую типизацию.
Потому что так легко злоупотреблять operator <
, который не соответствует требованиям стандартного алгоритма, например. случайно используя его через std::sort
, std::lower_bound
и т.д., я рекомендую определить operator <
либо как строгий слабый порядок, либо совсем не. Примером CodesInChaos является частичный порядок, который не соответствует требованию "транзитивности несравнимости" строгого слабого порядка. Поэтому я бы рекомендовал называть такое отношение другим именем, например. bool setLess(const MySet &, const MySet &)
.
Источники:
С++ не делает этого автоматически. Для operator>
, operator<=
и operator>=
вы можете использовать std::rel_ops
; для этого требуется только operator<
. Однако он не обеспечивает operator==
в терминах operator<
. Вы можете сделать это так:
template <class T>
bool operator==(T const& lhs, T const& rhs)
{
return !((lhs < rhs) or (rhs < lhs));
}
Обратите внимание, что: !((lhs < rhs) or (rhs < lhs))
и !(lhs < rhs) and !(rhs < lhs)
являются математически эквивалентными.
Компилятор не выводит ==
из <
.
Вы можете проверить это с помощью простого примера:
#include <iostream>
struct A {
A(int r):i{r}{}
int i;
};
bool operator<(A const & a1, A const& a2) {
return a1.i < a2.i;
}
int main(int argc, char* argv[]) {
A a1{2};
A a2{3};
if(a1 == a2) {
std::cout << "equals\n";
}
return 0;
}
GCC
дает вам эту ошибку:
main.cpp:20:11: error: no match for 'operator==' (operand types are 'A' and 'A')
if(a1 == a2) {
В std::rel_ops
есть шаблоны, которые автоматически определяют отсутствующие операторы.
Он не определяет оператор равенства, основанный на меньшем операторе, как вы пожелаете.
Тем не менее это весьма полезно; если вы определяете оператор меньшего оператора и оператора равенства, у вас будут другие операторы сравнения бесплатно.
Как утверждают многие, вы не можете, и компилятор не должен этого делать.
Это не означает, что не должно быть легко перейти от <
до ==
и всего мириада.
boost:: operator пытается облегчить задачу. Используйте его и сделайте.
Если вы хотите сделать это самостоятельно, для этого потребуется лишь немного кода для повторной реализации того, что дает вам повышение:
namespace utility {
namespace details {
template<class...>using void_t=void;
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = ::utility::details::can_apply<Z,void,Ts...>;
}
namespace auto_operators {
template<class T, class U>
using less_r = decltype( std::declval<T const&>() < std::declval<U const&>() );
template<class T, class U>
using can_less = ::utility::can_apply<less_r, T, U>;
struct order_from_less {
template<class T, class U>
using enabled = std::enable_if_t<
std::is_base_of<order_from_less, T>{}
&& std::is_base_of<order_from_less, U>{}
&& can_less<T, U>{},
bool
>;
template<class T, class U>
friend enabled<U,T>
operator>(T const& lhs, U const& rhs) {
return rhs < lhs;
}
template<class T, class U>
friend enabled<U,T>
operator<=(T const& lhs, U const& rhs) {
return !(lhs > rhs);
}
template<class T, class U>
friend enabled<T,U>
operator>=(T const& lhs, U const& rhs) {
return !(lhs < rhs);
}
};
struct equal_from_less:order_from_less {
template<class T, class U>
using enabled = std::enable_if_t<
std::is_base_of<order_from_less, T>{}
&& std::is_base_of<order_from_less, U>{}
&& can_less<T, U>{} && can_less<U,T>{},
bool
>;
template<class T, class U>
friend enabled<U,T>
operator==(T const& lhs, U const& rhs) {
return !(lhs < rhs) && !(rhs < lhs);
}
template<class T, class U>
friend enabled<U,T>
operator!=(T const& lhs, U const& rhs) {
return !(lhs==rhs);
}
};
}
Вышеупомянутое должно быть записано только один раз, или эквивалентная коса получена из #include
boost.
Как только у вас есть boost или выше, это так же просто, как что-то вроде:
struct foo : auto_operators::equal_from_less {
int x;
foo( int in ):x(in) {}
friend bool operator<( foo const& lhs, foo const& rhs ) {
return lhs.x < rhs.x;
}
};
и foo
теперь имеют на нем все операторы упорядочения и сравнения.
int main() {
foo one{1}, two{2};
std::cout << (one < two) << "\n";
std::cout << (one > two) << "\n";
std::cout << (one == two) << "\n";
std::cout << (one != two) << "\n";
std::cout << (one <= two) << "\n";
std::cout << (one >= two) << "\n";
std::cout << (one == one) << "\n";
std::cout << (one != one) << "\n";
std::cout << (one <= one) << "\n";
std::cout << (one >= one) << "\n";
}
Точка всего этого состоит в том, что С++ не является, как язык, предполагать, что <
означает >
и >=
и ==
все имеет смысл. Но вы можете написать библиотеку, которая позволяет вам выбрать тип с <
, и добавление тривиального базового класса внезапно сделает все эти другие операции, определенные с нулевой стоимостью времени выполнения.
Ответ НЕТ, вам просто нужен простой тест
struct MyType{
int value;
};
bool operator < (MyType& a, MyType& b)
{
return a.value < b.value;
}
int main(int argc, char* argv[])
{
MyType a = {3};
MyType b = {4};
if (a == b)
std::cout << "a==b" << std::endl;
if (a < b)
std::cout << "a < b" << std::endl;
}
g++ 4.8.2 жалуется:
main.cpp: В функции 'int main (int, char **):
main.cpp: 16: 11: ошибка: нет соответствия для 'operator == (типы операндов - это MyType и MyType)
Но есть что-то подобное, что работает на С++, проверьте это понятия С++: Сравнить
он говорит:
equiv (a, b), выражение, эквивалентное! comp (a, b) & &! comp (b, a)
Рассмотрим следующий пример:
class point{
unsigned int x;
unsigned int y;
public:
bool operator <(const point& other){
return (x+y) < (other.x+other.y);
}
bool operator == (const point& other){
return (x==other.x) && (y==other.y);
}
}
И тогда мы имеем:
point a{1, 2};
point b{2, 1};
! (a < b),! (b < a), но также и (a == b).
В дополнение к другим ответам,
Компилятор не может даже вывести !=
из ==
struct MyType
{
int value;
};
bool operator == (const MyType& a, const MyType& b)
{
return a.value == b.value;
}
int main()
{
MyType a = {3};
MyType b = {4};
if (a != b) // (* compilation Error *)
std::cout << "a does not equal b" << std::endl;
}
Было бы неплохо, если бы была возможность сообщить компилятору, что остальные рациональные операторы применимы к вашему классу.
В некоторых ответах в заголовке <utility>
есть что-то, что может обеспечить такую функциональность. вам нужно будет добавить следующую строку в начале main
:
using namespace std::rel_ops;
Однако использование этого подхода является дорогостоящим и вызовет перегрузку неоднозначностей по всему месту, как отмечает JDługosz.
Вид.
Но вам понадобится boost:: operator
Перегруженные операторы для типов классов обычно встречаются в группах. если ты может написать x + y, вы, вероятно, также захотите написать x + = y. Если вы можете написать x < y, вы также хотите, чтобы x > y, x >= y, и x <= y. Более того, если у вашего класса действительно удивительное поведение, некоторые из эти связанные операторы могут быть определены в терминах других (например, x >= y <= > ! (x < y)). Репликация этого шаблона для нескольких классов как утомительный, так и подверженный ошибкам. Шаблоны boost/operations.hpp помогают путем создания операторов для вас в области пространства имен на основе других операторов, которые вы определили в своем классе.
Ответ ясный НЕТ. Нет никакого неявного способа. Классы С++ позволяют операторам перегружать. Итак, ваша идея, что логически, if !(a < b) and !(b < a)
означает a == b
. верно. И вы можете перегрузить операторы, как показано ниже. Например, класс Fraction:
class Fraction {
int num;
int denom;
. . .
public:
. . .
bool operator < (const Fraction &other) {
if ((this->num * other.denom) < (this->denom * other.num))
return false;
else
return true;
}
bool operator == (const Fraction &other) (
if (!(*this < other) && !(other < *this)) {
return true;
else
return false;
}
};