operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 24.05.25 14:19
Оценка:
Здравствуйте!

Есть свой класс, он реализован так, что ведёт себя как интегральный тип. Есть конструктор из интегрального типа. Надо также сделать оператор преобразования в интегральный тип operator int/operator T.

Вопрос — сделать этот оператор explicit или нет? Если сделать explicit, то он уже не будет вести себя как интегральный — компилятор не сможет его неявно преобразовать в интегральный тип.

С другой стороны, если в выражении участвует мой тип и интегральные типы, то мне надо, чтобы интегральные преобразовывались в мой тип, а не наоборот, так как мой тип шире. Что будет выбирать компилятор, если ему доступен конструктор моего типа из интегрального и не-explicit оператор преобразования в интегральный тип?
Маньяк Робокряк колесит по городу
Re: operator int
От: Muxa  
Дата: 24.05.25 14:31
Оценка:
M>Что будет выбирать компилятор, если ему доступен конструктор моего типа из интегрального и не-explicit оператор преобразования в интегральный тип?

А это как-то влияет не результат вычислений в рантайме?
Re[2]: operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 24.05.25 14:33
Оценка:
Здравствуйте, Muxa, Вы писали:

M>>Что будет выбирать компилятор, если ему доступен конструктор моего типа из интегрального и не-explicit оператор преобразования в интегральный тип?


M>А это как-то влияет не результат вычислений в рантайме?


У меня "интегральный" тип с произвольным размером. Если будет преобразование в интегральный тип, то будет усечение, если мой — усечения не будет. Да, в этом плане мой тип ведёт себя не совсем как интегральный, тут я наврал
Маньяк Робокряк колесит по городу
Re[3]: operator int
От: Muxa  
Дата: 24.05.25 14:45
Оценка: :)
M>>А это как-то влияет не результат вычислений в рантайме?

M>У меня "интегральный" тип с произвольным размером. Если будет преобразование в интегральный тип, то будет усечение, если мой — усечения не будет. Да, в этом плане мой тип ведёт себя не совсем как интегральный, тут я наврал


Тогда компилятор будет выбирать операторы в зависимости от порядка вычислений в выражениях, если ему нужно будет сложить два инта то он сложит два инта. И насколько я знаю, поправьте меня знатоки стандарта, этот порядок не всегда определен. Например, x = v + u + z — хз в каком порядке будет вычислено в итоге.
Отредактировано 24.05.2025 14:46 Muxa . Предыдущая версия .
Re[4]: operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 24.05.25 14:55
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Тогда компилятор будет выбирать операторы в зависимости от порядка вычислений в выражениях, если ему нужно будет сложить два инта то он сложит два инта. И насколько я знаю, поправьте меня знатоки стандарта, этот порядок не всегда определен. Например, x = v + u + z — хз в каком порядке будет вычислено в итоге.


Я не большой знаток стандарта, но имхо компилятор будет вычислять точно так, как ему сказано. Порядок вычисления не определён при вычислении аргументов функции, но это другое

Если бы компилятор мог произвольно менять порядок вычисления в вырважениях, то не работали (или произвольно работали бы, на разных компиляторах по разному, и/или в зависимости от уровня оптимизации) бы подобные конструкции:
if ((a*10/10)!=a)
{
    //...
}



Но ты меня натолкнул на мысль — надо переопределить ещё все MyInt operator X(integral_type right) для интегральных типов, и сделать двухместные friend операторы MyInt operator X(integral_type left, MyInt right)[/tt] для моего MyInt.

Тогда всё будет работать как надо, и можно сделать operator integral_type() не explicit, и всё будет работать нормасик
Маньяк Робокряк колесит по городу
Re[5]: operator int
От: Muxa  
Дата: 24.05.25 15:16
Оценка:
M>
M>if ((a*10/10)!=a)
M>{
M>    //...
M>}
M>


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

Я, кстати, тоже свой тип писал для fp32 вычислений, чтобы эмулировать на цпу вычисления как они происходят на видеокарте с точностью до последнего бита мантиссы.
Re[6]: operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 24.05.25 15:23
Оценка:
Здравствуйте, Muxa, Вы писали:

M>Для умножения и деления может быть да — порядок определен, а для сложения и вычитания я хз.


Почему ты считаешь, что для умножения и деления правила другие, нежели чем для сложения и вычитания? У операторов с равным приоритетом порядок вычисления строго слева направо, иначе, если в выражении есть пользовательские типы, то разные компиляторы и/или один компилятор с разными уровнями оптимизации выводили бы даже разный тип результата выражений.


M>Я, кстати, тоже свой тип писал для fp32 вычислений, чтобы эмулировать на цпу вычисления как они происходят на видеокарте с точностью до последнего бита мантиссы.


Хм, а там не IEEE-шные fp32? Если IEEE-шные fp32, то чем они отличаются от IEEE-шных на CPU? А если не IEEE-шные, то тогда на разных видеочипах это может быть по разному, надо писать реализацию под каждый тип видеочипа
Маньяк Робокряк колесит по городу
Re[7]: operator int
От: Muxa  
Дата: 24.05.25 17:01
Оценка:
M>>Для умножения и деления может быть да — порядок определен, а для сложения и вычитания я хз.

M>Почему ты считаешь, что для умножения и деления правила другие, нежели чем для сложения и вычитания? У операторов с равным приоритетом порядок вычисления строго слева направо, иначе, если в выражении есть пользовательские типы, то разные компиляторы и/или один компилятор с разными уровнями оптимизации выводили бы даже разный тип результата выражений.


Поэтому я сказал что не знаю как в стандарте этот момент описан.


M>>Я, кстати, тоже свой тип писал для fp32 вычислений, чтобы эмулировать на цпу вычисления как они происходят на видеокарте с точностью до последнего бита мантиссы.


M>Хм, а там не IEEE-шные fp32? Если IEEE-шные fp32, то чем они отличаются от IEEE-шных на CPU? А если не IEEE-шные, то тогда на разных видеочипах это может быть по разному, надо писать реализацию под каждый тип видеочипа


IEEE-шные, но на цпу это все может вычисляется множеством вариантов: интринсики/сопроцессор/расширенные 80-битные регистры и результат в общем случае не совпадает.
Re: operator int
От: rg45 СССР  
Дата: 26.05.25 07:36
Оценка: +3
Здравствуйте, Marty, Вы писали:

M>Есть свой класс, он реализован так, что ведёт себя как интегральный тип.


Так заведение оператора приведения к интегральному типу — это как индикатор того, что декларируемая цель не достигнута.

M>Вопрос — сделать этот оператор explicit или нет? Если сделать explicit, то он уже не будет вести себя как интегральный — компилятор не сможет его неявно преобразовать в интегральный тип.


С опереторами неявного приведения можно огрести таких сюрпризов, котрые нарочно и не придумаешь. Я считаю, что такой оператор — это какой-то очень редкий исключительный случай. И если у тебя нет уверенности. что этот тот самый случай (а у тебя её нет, насколько я могу судить), то лучше этого не делать.
--
Справедливость выше закона. А человечность выше справедливости.
Re: operator int
От: sergii.p  
Дата: 26.05.25 12:15
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте!


M>Есть свой класс, он реализован так, что ведёт себя как интегральный тип. Есть конструктор из интегрального типа. Надо также сделать оператор преобразования в интегральный тип operator int/operator T.


M>Вопрос — сделать этот оператор explicit или нет? Если сделать explicit, то он уже не будет вести себя как интегральный — компилятор не сможет его неявно преобразовать в интегральный тип.


M>С другой стороны, если в выражении участвует мой тип и интегральные типы, то мне надо, чтобы интегральные преобразовывались в мой тип, а не наоборот, так как мой тип шире. Что будет выбирать компилятор, если ему доступен конструктор моего типа из интегрального и не-explicit оператор преобразования в интегральный тип?


так вроде очевидно. Если он будет не explicit, то выражение 1 + my_integral_type{2} + 3 приведёт к ambitious при выборе функции
Re[4]: operator int
От: rg45 СССР  
Дата: 26.05.25 13:35
Оценка: 6 (1) +1
Здравствуйте, Muxa, Вы писали:

M>Тогда компилятор будет выбирать операторы в зависимости от порядка вычислений в выражениях, если ему нужно будет сложить два инта то он сложит два инта. И насколько я знаю, поправьте меня знатоки стандарта, этот порядок не всегда определен. Например, x = v + u + z — хз в каком порядке будет вычислено в итоге.


Тут важно не путать порядок выполнения операций и порядок вычисления значений операндов. Порядок выполнения операций определяется их приоритетами и ассоциативностью и строго определён: https://en.cppreference.com/w/cpp/language/operator_precedence. Совсем другое дело — порядок вычисления операндов (подвыражений) — он может быть любым (unspecified): https://en.cppreference.com/w/cpp/language/eval_order.html.

Например, в выражении foo(a) + bar(b) * baz(c) однозначно сначала выполнится умножение, потом сложение. А вот в каком порядке будут вызваны функции foo, bar, baz — это как решит компилятор.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.05.2025 13:38 rg45 . Предыдущая версия . Еще …
Отредактировано 26.05.2025 13:38 rg45 . Предыдущая версия .
Re[5]: operator int
От: B0FEE664  
Дата: 26.05.25 19:28
Оценка: +1
Здравствуйте, rg45, Вы писали:

M>>Тогда компилятор будет выбирать операторы в зависимости от порядка вычислений в выражениях, если ему нужно будет сложить два инта то он сложит два инта. И насколько я знаю, поправьте меня знатоки стандарта, этот порядок не всегда определен. Например, x = v + u + z — хз в каком порядке будет вычислено в итоге.


R>Тут важно не путать порядок выполнения операций и порядок вычисления значений операндов. Порядок выполнения операций определяется их приоритетами и ассоциативностью и строго определён: https://en.cppreference.com/w/cpp/language/operator_precedence. Совсем другое дело — порядок вычисления операндов (подвыражений) — он может быть любым (unspecified): https://en.cppreference.com/w/cpp/language/eval_order.html.


R>Например, в выражении foo(a) + bar(b) * baz(c) однозначно сначала выполнится умножение, потом сложение. А вот в каком порядке будут вызваны функции foo, bar, baz — это как решит компилятор.


Так, но не совсем так.

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

Для выражения x = v + u + z можно подобрать такие v + u + z, что в зависимости от порядка выполнения операций промежуточный результат может как оставаться в пределах или же выходить за пределы. В стандарте есть разъяснения по этому поводу.
  разъяснения

5 [Note 4 : The implementation can regroup operators according to the usual mathematical rules only where the
operators really are associative or commutative.41 For example, in the following fragment
int a, b;
/* ... */
a = a + 32760 + b + 5;
the expression statement behaves exactly the same as
a = (((a + 32760) + b) + 5);
due to the associativity and precedence of these operators. Thus, the result of the sum (a + 32760) is next added to
b, and that result is then added to 5 which results in the value assigned to a. On a machine in which overflows produce
an exception and in which the range of values representable by an int is [-32768, +32767], the implementation cannot
rewrite this expression as
a = ((a + b) + 32765);
since if the values for a and b were, respectively, −32754 and −15, the sum a + b would produce an exception while
the original expression would not; nor can the expression be rewritten as either
a = ((a + 32765) + b);
or
a = (a + (b + 32765));
since the values for a and b might have been, respectively, 4 and −8 or −17 and 12. However on a machine in which
overflows do not produce an exception and in which the results of overflows are reversible, the above expression
statement can be rewritten by the implementation in any of the above ways because the same result will occur. —end
note]


Вывод: порядок зависит от архитектуры.

Отдельно, по теме, стоить заметить, что

41) Overloaded operators are never assumed to be associative or commutative.

А раз у Marty есть свои операторы, то они запрещают компилятору менять порядок. Казалось бы. На самом деле компилятор может менять порядок как ему заблагорассудится до тех пор, пока результат остаётся таким, как если бы порядок не менялся.
И каждый день — без права на ошибку...
Re: operator int
От: Pzz Россия https://github.com/alexpevzner
Дата: 26.05.25 19:48
Оценка:
Здравствуйте, Marty, Вы писали:

M>С другой стороны, если в выражении участвует мой тип и интегральные типы, то мне надо, чтобы интегральные преобразовывались в мой тип, а не наоборот, так как мой тип шире. Что будет выбирать компилятор, если ему доступен конструктор моего типа из интегрального и не-explicit оператор преобразования в интегральный тип?


Я как-то пробовал на C++ написать тип, который ведет себя, как int. Не то, что бы я мастер C++, но по-моему, это невозможно.
Re[2]: operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 26.05.25 19:53
Оценка:
Здравствуйте, rg45, Вы писали:

M>>Есть свой класс, он реализован так, что ведёт себя как интегральный тип.


R>Так заведение оператора приведения к интегральному типу — это как индикатор того, что декларируемая цель не достигнута.


Ну почему? У меня есть функция, принимающая int, например. При вызове этой функции вычисляется какое-то выражение. Я хочу сменить используемый в выражении тип с интегрального на свой MyInt. При отсутствии неявного operator int мне надо будет переписать выражение, добавив явный каст к интегральному типу. Это может иногда быть просто невозможно, в каких-нибудь чужих шаблонах.


R>С опереторами неявного приведения можно огрести таких сюрпризов, котрые нарочно и не придумаешь. Я считаю, что такой оператор — это какой-то очень редкий исключительный случай. И если у тебя нет уверенности. что этот тот самый случай (а у тебя её нет, насколько я могу судить), то лучше этого не делать.


А можно примеры сюрпризов? Вот с приведением к bool через void* сюрпризы бывали, и для этого добавили explicit operator bool, но, не смотря на ключевое слово explicit, это работает без явного преобразования к bool в контексте условных операторов.
Маньяк Робокряк колесит по городу
Re[2]: operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 26.05.25 19:57
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>так вроде очевидно. Если он будет не explicit, то выражение 1 + my_integral_type{2} + 3 приведёт к ambitious при выборе функции


А если у меня есть все версии operator+, которые могут принимать MyInt и integral_type в любой комбинации? Тоже будет амбигус? Но, по идее, тут ведь не нужен ни ctor MyInt(integral_type), ни MyInt::operator integral_type().
Маньяк Робокряк колесит по городу
Re[2]: operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 26.05.25 20:02
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Я как-то пробовал на C++ написать тип, который ведет себя, как int. Не то, что бы я мастер C++, но по-моему, это невозможно.


Чой-то?

Можешь попинать и/или потестить мой вариант, и найти, где он ведёт себя не как int (кроме, возможно, некоторых случаев со сдвигами и битовыми операторами). Но возможные несовместимости связаны только с тем, что у меня тип произвольной размерности, если бы я делал длинный int-тип фиксированного размера, то он был бы вообще идентичен встроенному.
Маньяк Робокряк колесит по городу
Отредактировано 26.05.2025 20:04 Marty . Предыдущая версия .
Re[3]: operator int
От: rg45 СССР  
Дата: 26.05.25 20:14
Оценка:
Здравствуйте, Marty, Вы писали:

M>Ну почему? У меня есть функция, принимающая int, например. При вызове этой функции вычисляется какое-то выражение. Я хочу сменить используемый в выражении тип с интегрального на свой MyInt. При отсутствии неявного operator int мне надо будет переписать выражение, добавив явный каст к интегральному типу. Это может иногда быть просто невозможно, в каких-нибудь чужих шаблонах.


Ну смотри — для инта функция есть, а для твоего типа у тебя такой функции нет. Уже получается, что твой тип не подобен инту. Ты скармливаешь этой функции инт ВМЕСТО своего типа. Не важно, явно или неявно.

M>А можно примеры сюрпризов?


Ну вот из недавнего. Разбирали один супер-пупер говно-класс типа универсальной обертки разного вида строк. И чего там в этом классе только не было — и функции и операторы, в т.ч. и неявного приведения. А вот чего не было в этом классе — так это операторов three-way сравнения. А именно эти операторы использовались в ассоциативных контейнерах стандартной библиотеки. В результате, при использовании объектов этого чудо-класса в качестве ключа мапы использовался не мудреный оператор сравнения, а оператор преобразования к const char*.

Мало? Еще пример: написали функцию, возвращающую "универсальную" ссылку, рассчитывая на то, что на выходе получится lvalue ссылка на оригинальный объект. Но в игру вмешался оператор неявного преобразования и вместо этого получилась rvalue ссылка на верменный объект. И оказалась эта ссылка битой, сразу же после выхода из функции.

Да и главная сложность не в том или другом частном примере, а в многообразии труднопредсказуемых "сюрпризов", которые способны подарить неявные преобразования.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 26.05.2025 20:23 rg45 . Предыдущая версия .
Re[3]: operator int
От: Pzz Россия https://github.com/alexpevzner
Дата: 26.05.25 20:37
Оценка:
Здравствуйте, Marty, Вы писали:

M>Можешь попинать и/или потестить


Не хотю

P.S. Тебе RSA приспичило переписать? Зачем тебе big int?
Re[3]: operator int
От: rg45 СССР  
Дата: 26.05.25 20:44
Оценка:
Здравствуйте, Marty, Вы писали:

M>А если у меня есть все версии operator+, которые могут принимать MyInt и integral_type в любой комбинации? Тоже будет амбигус? Но, по идее, тут ведь не нужен ни ctor MyInt(integral_type), ни MyInt::operator integral_type().


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

https://coliru.stacked-crooked.com/a/1b55e45ab30f2a79

#include <iostream>

struct MyInt {
    int value{};

    operator int() const {return value;}
};

MyInt operator + (const MyInt& a, int b) { return {a.value + b}; }

int main()
{
    std::cout << MyInt{42} + sizeof(MyInt) << std::endl;
}
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: operator int
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 26.05.25 20:50
Оценка:
Здравствуйте, Pzz, Вы писали:

M>>Можешь попинать и/или потестить


Pzz>Не хотю


Дело хозяйское


Pzz>P.S. Тебе RSA приспичило переписать?


Та не, нафик мне тот RSA


Pzz>Зачем тебе big int?


Шобы был. Надоело отслеживать всякие переполнения. Ну и у меня есть уже Decimal, он заточен под десятичные числа с плавающей точкой произвольной длины, но из-за десятичности он не оптимален для хранения целых. Захотел ему в пару более эффективный целый тип.
Маньяк Робокряк колесит по городу
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.