Рассмотрим минимальный пример ниже:
#include<utility>
struct S { };
int main() {
S s;
std::move(s) = S{};
}
Компилируется без ошибок.
Если вместо этого я использую не классовые типы, я получаю ошибку.
Например, приведенный ниже код не компилируется:
#include<utility>
int main() {
int i;
std::move(i) = 42;
}
То же самое происходит с перечислениями, перечислениями с областью видимости и так далее.
Ошибка (из GCC):
используя xvalue (ссылка на rvalue) в качестве lvalue
Что за этим стоит?
Я думаю, это правильно, но я хотел бы понять, по какой причине я могу сделать это со всеми типами, кроме неклассовых.
C ++ позволяет присваивать объекту rvalue объекта, но не примитиву rvalue;
Пример,
string s1, s2;
s1 + s2 = "asdf"; // ok since rvalue s1 + s2 is an object
int i1, i2;
i1 + i2 = 10; // error, since i1 + i2 is a primitive type
То же правило относится к вашему вопросу. std :: move (s) возвращает r-значение типа объекта, но std :: move (i) возвращает r-значение примитивного типа.
Я пытаюсь ответить на свой вопрос с кучей ссылок на стандарт.
Я уверен, что напишу что-нибудь ужасно неправильно, и кто-то придет со мной, говоря словами.
Ну, я сделал все возможное, чтобы объяснить, как можно вывести из стандарта то, что описано в вопросе.
Не стесняйтесь понижать голос, если это необходимо, но, пожалуйста, дайте мне знать, что не так, чтобы иметь возможность исправить ответ и понять ошибку.
Спасибо.
3.9 / 8 (типы):
тип объекта является (возможно, cv-квалифицированным) типом, который не является типом функции, не является ссылочным типом и не резюме недействительным.
5.2.2 / 10 (выражения, вызов функции):
Вызов функции является […] значением xvalue, если тип результата является ссылкой на тип объекта
таким образом std::move
является xvalue выражение в обоих случаях.
5,18 / 3 (Назначение):
Если левый операнд не относится к типу класса, выражение неявно преобразуется […] в cv-неквалифицированный тип левого операнда.
Это не добавляет полезной информации, но это ради полноты.
4,1 / 2 (преобразование lvalue в rvalue):
В противном случае, если T имеет тип класса, преобразование копирует и инициализирует временное значение типа T из glvalue, и результат преобразования является предварительным значением для временного.
В противном случае значение, содержащееся в объекте, указанном в glvalue, является результатом prvalue.
12,2 (временные объекты) делает все остальное.
Итак, как упомянул @xaxxon в комментариях, я действительно пытался сделать (позвольте мне написать) 42 = 0;
и это не является допустимым выражением в C ++.
Как правильно указано в комментариях @bogdan, правая часть стандарта, на которую следует ссылаться в этом случае, 5,18 / 1 (Назначение):
Все требуют изменяемого lvalue как их левый операнд […]
В то время как 5/2 а также 5/3 уточнить, что это утверждение относится только к встроенным операторам.