Странное поведение с оператором›= перегрузка

У меня странное поведение при перегрузке оператора в C++. У меня есть класс, и мне нужно проверить, больше ли его содержимое или равно long double. Я перегрузил оператор >=, чтобы сделать эту проверку, моя декларация выглядит следующим образом:

bool MyClass::operator>=(long double value) const;

Я должен сказать, что у меня также есть оператор cast-to-long-double для моего класса, который работает без исключений только при определенных условиях. Теперь, когда я использую этот оператор, компилятор жалуется на неоднозначное использование оператора >= и альтернативы:

  • Мой.
  • Встроенный operator>=(long double, int).

Теперь, как мне заставить программу использовать мой оператор?


person tunnuz    schedule 15.02.2009    source источник


Ответы (6)


Обновление 2015 г. Или, если вы хотите сохранить возможность преобразования с использованием синтаксиса (double)obj вместо синтаксиса obj.to_double(), сделайте функцию преобразования explicit, добавив к ней префикс с этим ключевым словом. Вам нужно явное приведение, чтобы преобразование сработало. Лично я предпочитаю синтаксис .to_double, если только преобразование не будет в bool, потому что в этом случае преобразование используется if(obj), даже если это explicit, и, на мой взгляд, это значительно более читабельно, чем if(obj.to_bool()).


Отбросьте оператор преобразования. Это вызовет проблемы на всем пути. Иметь функцию, подобную

to_double()

Или аналогичный, который возвращает двойное значение и явно вызывает эту функцию, чтобы получить двойное значение.

Для рассматриваемой проблемы существует следующая проблема:

obj >= 10

Вдумайтесь в это выражение. Встроенный оператор сопоставляет первый аргумент с определенной пользователем последовательностью преобразования для вашего типа с использованием оператора преобразования long double(). Но ваша функция соответствует второму аргументу стандартной последовательностью преобразования из int в long double (преобразование целого числа в число с плавающей запятой). Всегда неоднозначно, когда есть преобразования для двух аргументов, но нет хотя бы одного аргумента, который можно преобразовать лучше, в то время как остальные аргументы не преобразуются хуже за один вызов. В вашем случае встроенная лучше соответствует второму аргументу, но хуже первому, но ваша функция соответствует первому аргументу лучше, но второму хуже.

Это сбивает с толку, поэтому вот несколько примеров (преобразования из char в int называются поощрениями, которые лучше, чем преобразования из char во что-то отличное от int, что называется преобразованием):

void f(int, int);
void f(long, long);
f('a', 'a');

Вызывает первую версию. Потому что все аргументы для первого можно лучше преобразовать. В равной степени следующее вызовет первое:

void f(int, long);
void f(long, long);
f('a', 'a');

Потому что первое конвертируется лучше, а второе хуже не конвертируется. Но следующее неоднозначно:

void f(char, long);
void f(int, char);
f('a', 'a'); // ambiguous

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

Это очень похоже на ваш случай выше. Несмотря на то, что стандартная последовательность преобразования (преобразование из int/float/double в long double) лучше, чем заданная пользователем последовательность преобразования (преобразование из MyClass в long double), ваша версия оператора не выбрана, потому что ваш другой параметр (long double) требует преобразования из аргумента, которое хуже, чем то, что требуется встроенному оператору для этого аргумента (идеальное совпадение).

Разрешение перегрузки — сложный вопрос в C++, поэтому невозможно запомнить все тонкие правила в нем. Но получить примерный план вполне возможно. Я надеюсь, что это поможет вам.

person Johannes Schaub - litb    schedule 15.02.2009

Предоставляя неявное преобразование в double, вы фактически заявляете, что мой класс эквивалентен double, и по этой причине вы не должны возражать, если используется встроенный оператор >= для doubles. Если вас это действительно волнует, то ваш класс на самом деле не «эквивалентен» double, и вам следует подумать о том, чтобы не предоставлять неявное преобразование в double, а вместо этого предоставить явная функция-член GetAsDouble или ConvertToDouble.

Причина, по которой у вас есть двусмысленность в данный момент, заключается в том, что для выражения t >= d, где t является экземпляром вашего класса, а d является двойным, компилятор всегда должен обеспечивать преобразование либо левой, либо правой стороны, поэтому выражение действительно двусмысленное. Либо вызывается operator double t и используется встроенный оператор >= для doubles, либо d должен быть повышен до long double и используется оператор-член >=.

Редактировать, вы обновили свой вопрос, чтобы предположить, что ваше преобразование в long double и ваше сравнение с int. В этом случае последний абзац следует читать:

Причина, по которой у вас есть двусмысленность в данный момент, заключается в том, что для выражения t >= d, где t является экземпляром вашего класса, а d является int, компилятор всегда должен обеспечивать преобразование либо левой, либо правой стороны, поэтому выражение действительно двусмысленное. Либо вызывается operator long double t и используется встроенный оператор >= для long double и int, либо d должен быть повышен до long double и используется оператор-член >=.

person CB Bailey    schedule 15.02.2009

Я предполагаю, что вы сравниваете с буквальным int, а не с long double:

MyClass o;

if (o >= 42)
{
   // ...
}

Если это так, то обе альтернативы одинаково хороши/сложны.

Используя operator long double():

  1. MyClass::operator long double()
  2. встроенный operator>=(long double, int)

Используя MyClass::operator>=(long double):

  1. встроенное преобразование int в long double
  2. MyClass::operator>=(long double)
person dalle    schedule 15.02.2009
comment
Я на самом деле сравниваю с длинным двойным. - person tunnuz; 15.02.2009

У вас есть long double в декларации. Попробуйте изменить его на double.

person Daniel Earwicker    schedule 15.02.2009
comment
Извините, я имел в виду long double везде. - person tunnuz; 15.02.2009

Использование вами перегрузки операторов в сочетании с настраиваемым приведением типов может сильно сбивать с толку пользователей вашего класса. Спросите себя, будут ли пользователи этого класса ожидать, что он превратится в двойника или будет сравним с двойником? Разве функция .greaterThan(double) не достигла бы той же цели, но не удивила бы пользователя?

Я думаю, вы всегда можете явно привести свой объект к удвоению перед сравнением, чтобы избежать двусмысленности. Но на вашем месте я бы пересмотрел подход, описанный выше, и сосредоточился на написании кода, который является интуитивно понятным и ведет себя неудивительно, вместо причудливого приведения типов и перегрузки операторов.

(Вдохновленный прекрасными разглагольствованиями FQA о перегрузке операторов)

person Assaf Lavie    schedule 15.02.2009

  • Встроенный оператор>=(long double, int).

Похоже, вы определили:

bool class::operator>=(long double value) { return value >= classValue; }

И тебе не хватает:

bool class::operator>=(double value)      { return value >= classValue; }
bool class::operator>=(int value)         { return value >= classValue; }

Таким образом, компилятор не может решить, в какую сторону конвертировать. (Это двусмысленно.)

Возможно, шаблонная функция (или метод) была бы полезна?

Остерегайтесь ситуаций, когда a›=b вызывает методы, отличные от методов b›=a.

person Mr.Ree    schedule 15.02.2009