Предположим, у нас есть следующий класс:
class Rational { // Represents rational number. n=1/2=d for example.
public:
int n = 0;
int d = 1;
};
Rational x = Rational();
x.n = 1;
x.d = 2;
Можно ли сделать такую перегрузку, чтобы 3 * x
вместо ошибки выдавал 3/2
?
Мой учитель сказал, что перегрузка происходит только между объектами одного типа, но почему мы можем делать перегрузку между cout
, который имеет тип ostream
, и объектом типа Rational
, а не типа int
и Rational
?
Если реализовать функции operator
, то можно сделать 3 * x
и получится 3 * n / d
Re: перегрузка происходит только между объектами одного типа. Перегрузка происходит только в функциях, и это по определению означает использование разных чисел, типов или аргументов. Вы, конечно, можете перегрузить оператор *
, где он принимает ваш класс и целочисленное значение.
«Мой учитель сказал, что перегрузка происходит только между объектами одного и того же типа». Либо это не то, что они сказали, либо они не правы, и Rational& operator*(Rational& lhs, float rhs) {lhs.n *= rhs; return lhs; }
, кажется, делает то, что просят.
Это выглядит довольно странно operator*
, если он изменяет один из своих аргументов. Я ожидаю, что operator*
вернет Rational
, а operator*=
изменит левую часть и вернет Rational&
.
Конечно. *
обычно должен брать const Rational &
. Хотя я вижу преимущество наличия Rational operator *(Rational &&lhs, float rhs)
, например. большая матрица.
Вы можете написать, например
Rational operator *( const Rational &r, int x )
{
return { r.n * x, r.d };
}
Rational operator *( int x, const Rational &r )
{
return { r.n * x, r.d };
}
Вы можете перегружать операторы для пользовательских типов. Для бинарного оператора по крайней мере один из операндов должен иметь определенный пользователем тип.
Из стандарта C++ 20 (12.4.2.3 Операторы в выражениях)
2 If either operand has a type that is a class or an enumeration, a user-defined operator function can be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator. Therefore, the operator notation is first transformed to the equivalent function-call notation as summarized in Table 15 (where @ denotes one of the operators covered in the specified subclause). However, the operands are sequenced in the order prescribed for the built-in operator (7.6).
Спасибо. Нужно ли писать две функции для случая где 3*x
и случая x*3
или будет достаточно одной функции. Я только что попробовал, и кажется, что одной функции достаточно, но всегда хорошо спросить.
@ShinobiSan Поскольку оператор коммутативен, вам следует определить оба оператора.
Я не очень понял, если он коммутативен, то одной функции должно быть достаточно, верно?
@ShinobiSan Недостаточно. То есть если вы перегрузите оператор для выражения 3*x, то для выражения x*3 компилятор выдаст ошибку, потому что для такого порядка аргументов этих типов такого оператора нет.
Разрешение перегрузки С++ не предполагает, что *
является коммутативным. (Это не всегда так.) Поэтому, если у вас есть operator*(const Rational&, int)
, но нет operator*(int, const Rational&)
, и вы пытаетесь написать 3 * someRational
, компилятор не будет пытаться сказать: «Я не знаю, что это значит, но смогу ли я что-то сделать, если Я попробовал someRational * 3
вместо этого?»
@ShinobiSan Можно (и нужно) реализовать логику функции только один раз и ссылаться на эту реализацию также во второй функции, которая применяется для взаимозаменяемых параметров.
Да, я бы посоветовал, чтобы в ответе Влада вторая функция просто читалась return r * x;
Вам нужно реализовать арифметические операторы для вашего класса. См. здесь (ищите раздел(ы) "арифметика"): stackoverflow.com/questions/4421706/….