Подтвердить что ты не робот

Как избежать перегрузки оператора присваивания, переходящего значение r в значение lvalue?

Насколько мне известно, возвращение *this является нормой написания перегруженного operator=. Но это может повысить значение до lvalue!

struct Foo {
  Foo& operator=(const Foo &t) { return *this; }
};

int main() {
  const Foo &ref = Foo();
  //Foo &ref1 = Foo();  // This line won't compile.
  //But the following line compiles. Isn't it dangerous?
  //Also please note that if = is not overloaded, the synthesized operator= will NOT let it compile.
  Foo &ref2 = (Foo() = Foo());

  return 0;
}
4b9b3361

Ответ 1

Ты прав. Короткий ответ заключается в том, что это законно, но опасно по причинам, которые вы указываете.

Другой (аналогичный) пример был добавлен в С++ 11 как часть стандарта не менее, с выходом потока rvalue, который возвращает ссылку lvalue.

template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);

Итак, std::ostream& s = std::ofstream("foo.txt") << "Oh boy"; является законным, но создает оборванную ссылку.

Я считаю, что компиляторы С++ 11 позволят ограничить функции-члены случаем, когда объект является lvalue, что должно позволить вам вообще предотвратить эту проблему. В приведенном ниже примере допускается присвоение, когда *this является значением l, поэтому оно предотвращает случайное преобразование.

struct Foo {
  Foo& operator=(const Foo &t) & { return *this; }
};

Ответ 2

Как следует из первого ответа, то, что вы делаете, является законным, но приведет к обманутой ссылке. Вы можете добиться такого же результата без перегрузки оператора присваивания:

#include <iostream>

struct Foo {
  Foo(int arg) : val(arg) { std::cout << "ctor " << val << "\n"; }
  ~Foo() { std::cout << "dtor " << val << "\n"; }
  int val;
};

int main() {
  Foo &ref = (Foo(1) = Foo(2));
  std::cout << "undefined behavior: " << ref.val << "\n";

  return 0;
}

Производит следующий вывод:

ctor 1
ctor 2
дтор 2
дтор 2
undefined поведение: 2

Как вы можете видеть, создается временная строка для Foo(1), а затем временная для Foo(2). Однако оба объекта уничтожаются, прежде чем мы сможем сделать что-либо с ссылкой, поэтому последующая строка приведет к поведению undefined.