Вопрос

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

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

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

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

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

Это было полезно?

Решение

Обновление 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++, поэтому невозможно запомнить все его тонкие правила.Но получить приблизительный план вполне возможно.Надеюсь, это вам поможет.

Другие советы

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

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

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

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

Я предполагаю, что вы сравниваете с буквальным 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)

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

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

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

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

  • Встроенный оператор >=(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; }

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

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

Остерегайтесь ситуаций, когда а>= б вызывает другие методы, чем б>= а.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top