Почему operator = возвращает *это?

скажем, я хочу переопределить operator = Так что я могу сделать что-то вроде

Poly p1;  // an object representing a polynomial
Poly p2;  // another object of the same type    
p2 = p1;  // assigns all the contents of p1 to p2

тогда в моей реализации operator =, у меня что-то вроде этого:

Poly& Poly::operator=(const Poly &source) {
    // Skipping implementation, it already works fine…
    return *this;
}

не возражайте против реализации, она уже работает нормально.

меня беспокоит то, что что происходит, когда вы return *this? Я знаю, что он возвращает ссылку на объект, но это то, что происходит?

p2 = &p1

5 ответов


вы return *this таким образом, вы можете написать нормальное соединение C++ = заявления типа:

Poly p1; //an object representing a polynomial
Poly p2;
Poly p2;

// ...

p3 = p2 = p1;  //assigns all the contents of p1 to p2 and then to p3

потому что это утверждение в основном:

p3.operator=(p2.operator=(p1));

если p2.operator=(...) не return *this у вас не будет ничего значимого, чтобы перейти в p3.operator=(...).


p2 = p1 это сокращение от p2.operator=(p1). Это просто вызов вашего operator= функция, которая возвращает ссылку на p2, который вы потом игнорируете. Чтобы прояснить это, давайте назовем это assign вместо operator=:

Poly& Poly::assign(const Poly &source) {
    .
    .
    .
    return *this;
}

теперь вместо p2 = p1, ты бы написал

p2.assign(p1);

в этом случае, результат вызова assign игнорируется, но вы не должны игнорировать его. Например, вы можете написать:

p3.assign(p2.assign(p1));

используя operator= вместо assign, это становится

p3 = (p2 = p1);

но поскольку присваивание является правоассоциативным, это также может быть записано как

p3 = p2 = p1;

эта форма возможности выполнять сразу несколько заданий первоначально происходит от C и была сохранена в C++ через соглашение о возврате *this на operator=().


может возникнуть соблазн сделать оператор копирования-назначения return void Если вам никогда не понадобятся цепные задания (как показано в других ответах) в любом случае. В конце концов, цепные задания часто трудно читать и понимать, поэтому не разрешать их можно считать улучшение.

однако часто упускается из виду, что void operator=(Poly& const) означает, что ваш тип больше не будет fulfuill в CopyAssignable концепция, которым требуется T& тип возвращаемого значения.

тип, который не выполнит CopyAssignable концепция не может официально использоваться для некоторых операций стандартного контейнера, например std::vector::insert, что означает, что следующий, казалось бы, невинный фрагмент кода дает неопределенное поведение, хотя он, вероятно, работает отлично:

#include <vector>

struct Poly
{
    void operator=(Poly const&) {} // Poly is not CopyAssignable
};

int main()
{
  std::vector<Poly> v;
  Poly p;
  v.insert(v.begin(), p); // undefined behaviour
}

как объясняет стандарт C++ в § 17.6.4.8 / 2.3, где он говорит об ограничениях на программы, использующие стандартную библиотеку:

(...) эффекты неопределено в следующих случаях:

(...) для типов, используемых в качестве аргументов шаблона при создании экземпляра a компонент шаблона если операции с типом не реализуют семантика применимых требований раздел (...).

конечно, это точно , потому что неопределенного поведения, которое компилятор может игнорировать ошибку и сделать программу ведите себя хорошо, подстраиваясь под явно намеченное поведение. Но этого не требуется.

вы должны также рассмотреть, что вы не можете предсказать все будущие использования Poly тип. Кто-то может написать некоторую функцию шаблона, такую как:

template <class T>
void f(T const& t)
{
    T t2;
    T t3 = t2 = t;
    // ...
}

эта функция не будет работать с вашим Poly класса.

просто не нарушайте это соглашение C++, и вы не столкнетесь с проблемами.


что происходит, когда вы вернетесь *этот?

в вашем примере (p2 = p1;), ничего. Метод копирует p1 на p2 и возвращает ссылку на объект "this", который вызывающий код не использует.

в коде, таких как p3 = p2 = p1; первая ссылка-это p2 = p1, копии p1 на p2 и возвращает ссылку на p2. Затем вызывающий код копирует из этой ссылки-на -p2 на p3 (и игнорирует ссылка на p3 что возвращается).

(попутно: ваши модульные тесты гарантируют, что p1 = p1 работает правильно? Легко забыть это дело!)


возврат ссылки на целевой объект позволяет назначить цепочку (каскадирование), а перегрузка операторов внутри класса следует за правой ассоциативной (нажмите здесь для подробных правил перегрузки оператора)

Poly a, b, c;
a = b = c;