У меня есть вопрос о возвращаемом значении оператора, перегружающегося в C++. Обычно я нашел два случая, каждый - возврат значением, и каждый - возврат ссылкой. Таким образом, каково нижнее правило этого? Особенно в случае, когда можно использовать оператор непрерывно, такой как cout<<x<<y
.
Например, когда реализация + операция "представляет в виде строки + (строка)". как Вы возвратили бы возвращаемое значение, касательно или val.
Некоторые операторы возвращают значение по значению, некоторые - по ссылке. Как правило, оператор, результатом которого является новое значение (например, +, - и т. Д.), Должен возвращать новое значение по значению, а оператор, результатом которого является существующее значение, но измененное (например, <<, >>, + =, - = и т. д.) должны возвращать ссылку на измененное значение.
Например, cout
- это std :: ostream
, а вставка данных в поток - это операция изменения, поэтому для реализации оператора <<
для вставки в ostream
оператор определяется следующим образом:
std::ostream& operator<< (std::ostream& lhs, const MyType& rhs)
{
// Do whatever to put the contents of the rhs object into the lhs stream
return lhs;
}
Таким образом, когда у вас есть составной оператор, такой как cout << x << y
, подвыражение Сначала вычисляется cout << x
, а затем вычисляется выражение [результат cout << x] << y
. Поскольку оператор <<
on x
возвращает ссылку на cout
, выражение [результат cout << x] << y
эквивалентно cout << y
, как и ожидалось.
И наоборот, для «строка + строка» результатом является новая строка (обе исходные строки не изменяются), поэтому она должна возвращаться по значению (в противном случае вы бы вернули ссылку на временное, что является неопределенным поведением).
Если вы хотите, чтобы перегрузка вашего оператора вела себя как встроенный оператор, тогда правило довольно простое; стандарт точно определяет, как ведут себя встроенные операторы, и указывает, является ли результат встроенного rvalue
или lvalue
.
Правило, которое вы должны использовать:
rvalue
, то ваша перегрузка должна возвращать ссылку lvalue
, то ваша перегрузка должна вернуть значение . Однако ваша перегрузка не обязана возвращать тот же результат, что и встроенный, хотя это то, что вам следует делать, если у вас нет веской причины сделать иначе.
Например, Kenny TM отметил в комментарии к другому ответу, что поток перегрузок для операторов <<
и >>
возвращает ссылку на левый операнд, а это не то, как встроенные работают. Но разработчики потокового интерфейса сделали это, чтобы можно было связать потоки ввода-вывода.
В зависимости от оператора может потребоваться возврат по значению.
Однако, когда можно использовать и то, и другое, например, в operator + =, вы могли бы рассмотреть следующее:
Обычно вы возвращаетесь по ссылке в операции, которая изменяет значение того, над чем она работает, например =
или + =
. Все остальные операции возвращаются по значению.
Это скорее практическое правило. Вы можете сконструировать своего оператора в любом случае.
Чтобы попытаться ответить на ваш вопрос о строках, оператор + () для строк почти всегда реализуется как бесплатная (не являющаяся членом) функция, поэтому неявная преобразования могут выполняться по любому параметру. Таким образом, вы можете сказать что-то вроде:
string s1 = "bar";
string s2 = "foo" + s1;
Учитывая это, и мы видим, что ни один параметр не может быть изменен, он должен быть объявлен как:
RETURN_TYPE operator +( const string & a, const string & b );
Мы игнорируем RETURN_TYPE на данный момент. Поскольку мы не можем вернуть ни один параметр (потому что мы не можем их изменить), реализация должна создать новое сцепленное значение:
RETURN_TYPE operator +( const string & a, const string & b ) {
string newval = a;
newval += b; // a common implementation
return newval;
}
Теперь, если мы сделаем RETURN_TYPE ссылкой, мы вернем ссылку на локальный объект, который является хорошо известное «нет-нет», поскольку локальный объект не существует вне функции. Поэтому наш единственный выбор - вернуть значение, то есть копию:
string operator +( const string & a, const string & b ) {
string newval = a;
newval += b; // a common implementation
return newval;
}