Для типов классов можно назначить временные объекты, которые на самом деле не разрешены для встроенных типов. Кроме того, оператор присваивания, сгенерированный по умолчанию, даже дает значение l:
int() = int(); // illegal: "expression is not assignable"
struct B {};
B& b = B() = B(); // compiles OK: yields an lvalue! ... but is wrong! (see below)
Для последнего оператора результат оператора присваивания фактически используется для инициализации ссылки не const
, которая будет устаревать сразу после оператора: ссылка не привязана к временному объекту напрямую (он не может поскольку временные объекты могут быть привязаны только к ссылкам const
или rvalue), но к результату назначения, срок жизни которого не расширяется.
Другая проблема заключается в том, что lvalue, возвращаемое из оператора присваивания, не выглядит так, как будто его можно перемещать, хотя на самом деле это относится к временному. Если что-то использует результат присваивания, чтобы получить значение, оно будет скопировано, а не перемещено, хотя было бы вполне жизнеспособным для перемещения. На этом этапе стоит отметить, что проблема описана в терминах оператора присваивания, потому что этот оператор обычно доступен для типов значений и возвращает ссылку на lvalue. Та же проблема существует для любой функции, возвращающей ссылку на объекты, т.е. *this
.
Потенциальное исправление состоит в том, чтобы перегрузить оператор присваивания (или другие функции, возвращающие ссылку на объект), чтобы рассмотреть тип объекта, например:
class G {
public:
// other members
G& operator=(G) & { /*...*/ return *this; }
G operator=(G) && { /*...*/ return std::move(*this); }
};
Возможность перегрузить операторы присваивания, как указано выше, появилась с С++ 11 и предотвратит отмеченное выше неактивное действие тонких объектов и одновременно позволит переносить результат назначения на временный. Реализация этих двух операторов, вероятно, одинакова. Хотя реализация, вероятно, будет довольно простой (по существу, всего лишь swap()
двух объектов), это все равно означает дополнительную работу, поднимающую вопрос:
Если функции, возвращающие ссылку на объект (например, оператор присваивания), наблюдают значительность объекта, которому присваивается?
Альтернативно (упомянутый Simple в комментарии) состоит в том, чтобы не перегружать оператор присваивания, а чтобы явно его явно присвоить &
, чтобы ограничить его использование до lvalues:
class GG {
public:
// other members
GG& operator=(GG) & { /*...*/ return *this; }
};
GG g;
g = GG(); // OK
GG() = GG(); // ERROR