2016-05-05 2 views
9

Рассмотрим минимальный пример ниже:xvalues: различия между не типами классов и типов классов

#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) в именующем

Что Обоснование этого?

Я предполагаю, что это правильно, но я хотел бы понять, в чем причина, по которой я могу это сделать со всеми типами, но с неклассическими.

+5

Это эквивалентно 's.operator = (S {})', и созданный компилятором 'operator =' определяется без ref-qualifier, поэтому он связывается с lvalues ​​и rvalues, как обычный метод. –

+0

Вы говорите, что стандарт не определяет его для типов неклассов, чтобы связывать как с lvalues, так и с rvalues? Можете ли вы дать больше (все) детали, предоставив ответ? Приветствуются ссылки на стандарт. – skypjack

+0

Неспособность назначить значения встроенного типа полностью возвращается к C. –

ответ

1

Я пытаюсь ответить на свой вопрос с кучей ссылок на стандарт.
Я совершенно уверен, что я напишу что-то, что ужасно неправильно, и кто-то придет со мной.
Ну, я сделал все возможное, чтобы объяснить, как можно вывести из стандарта то, что описано в вопросе.
Не стесняйтесь, если нужно, вниз, но, пожалуйста, дайте мне знать, что не так, чтобы иметь возможность исправить ответ и понять ошибку.
Спасибо.


3.9/8 (типы):

тип объекта является (возможно, резюме квалифицированным) типа, который не является тип функции, а не ссылочный типа, а не резюме void.

5.2.2/10 (выражения, вызов функции):

Вызов функции является [...] к xvalue, если типом результата является ссылкой на Rvalue типа объекта

Таким образом std::move является выражением xvalue в обоих случаях.

5.18/3 (назначение):

Если левый операнд не является тип класса, выражение неявно преобразуется [...] в сорте-неквалифицированный тип левого операнда.

Это не добавляет полезной информации, но это ради полноты.

4.1/2 (именующего к RValue конверсии):

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

В противном случае значение, содержащееся в объекте, обозначенном glvalue, является результатом prvalue.

12.2 (временные объекты) делает все остальное.

Итак, как упоминалось в комментариях @xaxxon, я на самом деле пытался (позвольте мне написать) 42 = 0;, и это не допустимое выражение в C++.

Как правильно указан в комментариях по @bogdan, правая часть стандарта, к которому следует относиться в этом случае 5.18/1 (Назначение):

Все требуют изменяемого именующее выражения в качестве левого операнд [...]

Хотя 5/2 и 5/3 уточнить, что заявление касается только встроенных операторов.

+1

На самом деле, я не думаю, что это хорошая идея подумать об этом с точки зрения «Я пытался сделать« 42 = 0; ». '42' - это * prvalue *; результат 'std :: move (i)' является * xvalue *; они оба * rvalues ​​*, но по существу разные. Я думаю, что стандартная ссылка, которую вы ищете, находится в [5.18p1]: * [...] Все требуют модифицируемого lvalue в качестве своего левого операнда [...] *. Как поясняется в [5p2] и [5p3], это утверждение относится только к встроенным операторам. – bogdan

+0

Не 4.1/2 правильно? В нем говорится, что glvalue заканчивается на prvalue, поскольку это не тип класса, как уже упоминалось. В любом случае, спасибо за комментарий, я посмотрю немного глубже в разделе, которое вы указали. – skypjack

+0

Почему на самом деле назначение присваивает преобразование lvalue-to-rval в операнде * left *? –

4

C++ позволяет присваивать классу объект rvalues, но не примитивный тип rvalues;

Пример,

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) возвращает rvalue типа объекта, но std :: move (i) возвращает rvalue примитивного типа.

+1

Дополнительная информация: было предложено применить квалификаторы lvalue-ref к стандартным операторам контейнера = по умолчанию, но это не считалось достаточно важным, чтобы продолжить с –

+1

. Я заметил это. Такое использование не является допустимым прецедентом, было бы неплохо иметь стандартные контейнеры, чтобы иметь возможность самостоятельно определять такие обычаи. – Harold

Смежные вопросы