Некоторое разъяснение по поводу rvalue ссылок

Во-первых: где std::move и std::forward определенный? Я знаю то, что они делают, но я не могу найти доказательство, что любой стандартный заголовок требуется, чтобы включать их. В gcc44 иногда std::move доступно, и иногда не, таким образом, категорическое включает директиву, было бы полезно.

При реализации семантики перемещения источник, по-видимому, оставляют в неопределенном состоянии. Это должно указать обязательно быть допустимым состоянием для объекта? Очевидно, необходимо смочь назвать деструктор объекта и смочь присвоить ему любыми средствами, которые выставляет класс. Но другие операции должны быть допустимыми? Я предполагаю то, что я спрашиваю, если Ваш класс гарантирует определенные инварианты, необходимо ли стремиться осуществить те инварианты, когда пользователь сказал, что они больше не заботятся о них?

Далее: когда Вы не заботитесь о семантике перемещения, есть ли какие-либо ограничения, которые заставили бы ссылку неконстанты быть предпочтенной по rvalue ссылке при контакте с параметрами функции? void function(T&); void function(T&&); С точки зрения вызывающей стороны способность передать временные ценности функций иногда полезна, таким образом, кажется, как будто нужно допустить, что опция каждый раз, когда выполнимо сделать так. И ссылки rvalue самостоятельно lvalues, таким образом, Вы не можете непреднамеренно вызвать конструктора перемещения вместо конструктора копии или чего-то как этот. Я не вижу оборотной стороны, но я уверен, что существует тот.

Который приносит мне к моему заключительному вопросу. Вы все еще не можете связать временные файлы со ссылками неконстанты. Но можно связать их с неконстантой rvalue ссылки. И можно затем провести ту ссылку как ссылку неконстанты в другой функции.

void function1(int& r) { r++; }
void function2(int&& r) { function1(r); }
int main() { 
    function1(5); //bad
    function2(5); //good
}

Помимо того, что это ничего не делает, там что-то не так с тем кодом? Мой пищеварительный тракт говорит, конечно, не, начиная с изменения rvalue ссылки вид самого главного к их существованию. И если переданное значение будет законно константой, то компилятор будет ловить его и вопить на Вас. Но судя по всему, это - отговорки механизма, который был, по-видимому, помещен на месте по причине, таким образом, я буду точно так же, как подтверждение, что я не делаю ничего глупого.

10
задан Johannes Schaub - litb 1 October 2011 в 15:03
поделиться

3 ответа

Во-первых: где определены std :: move и std :: forward?

См. 20.3 Компоненты утилит, <служебная программа> .


При реализации семантики перемещения источник предположительно остается в неопределенном состоянии. Должно ли это состояние обязательно быть допустимым состоянием для объекта?

Очевидно, что объект все еще должен быть разрушаемым. Но кроме этого, я думаю, что это хорошая идея, чтобы все еще можно было назначать. Стандарт говорит для объектов, которые удовлетворяют требованиям «MoveConstructible» и «MoveAssignable»:

[Примечание: rv остается допустимым объектом. Его состояние не указано. - конец примечания]

Это означало бы, я думаю, что объект все еще может участвовать в любой операции, которая не устанавливает никаких предварительных условий. Сюда входят CopyConstructible, CopyAssignable, Destructible и другие вещи. Обратите внимание, что с точки зрения базового языка для ваших собственных объектов ничего не потребуется. Требования вступают в силу только после того, как вы коснетесь компонентов стандартной библиотеки, в которых указаны эти требования.


Далее: если вы не заботитесь о семантике перемещения, существуют ли какие-либо ограничения, из-за которых неконстантная ссылка будет предпочтительнее ссылки rvalue при работе с параметрами функции?

Это, к сожалению, в решающей степени зависит от того, находится ли параметр в шаблоне функции и использует ли параметр шаблона:

void f(int const&); // takes all lvalues and const rvalues
void f(int&&); // can only accept nonconst rvalues

Однако для шаблона функции

template<typename T> void f(T const&);
template<typename T> void f(T&&);

Вы не можете этого сказать, потому что второй шаблон будет после того, как вызывается с lvalue, имеет в качестве параметра синтезированного объявления тип U & для неконстантных lvalue (и будет лучше) и U const & для const lvalues ​​(и будет неоднозначным). Насколько мне известно, не существует правила частичного упорядочивания, которое устраняло бы эту вторую двусмысленность. Однако это уже известно .

- Редактировать -

Несмотря на тот отчет о проблеме, я не думаю, что эти два шаблона неоднозначны. Частичное упорядочение сделает первый шаблон более специализированным, потому что после удаления модификаторов ссылок и const мы обнаружим, что оба типа одинаковы, а затем заметим, что первый шаблон имел ссылку на const. В Стандарте сказано ( 14.9.2.4 )

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

Если для каждого рассматриваемого типа данный шаблон является, по крайней мере, таким же специализированным для всех типов и более специализированным для некоторого набора типов, а другой шаблон не является более специализированным для каких-либо типов или, по крайней мере, не столь же специализирован для каких-либо типов, тогда данный шаблон более специализирован, чем другой шаблон.

Это делает шаблон T const & победителем частичного упорядочивания (и GCC действительно правильно выбрал его).

- Edit End -


Что подводит меня к моему последнему вопросу. Вы по-прежнему не можете привязать временные объекты к неконстантным ссылкам. Но вы можете привязать их к неконстантным ссылкам rvalue.

Это прекрасно объясняется в этой статье . Второй вызов с использованием function2 принимает только неконстантные r-значения. Остальная часть программы не заметит, если они будут изменены, потому что они больше не смогут получить доступ к этим rvalue впоследствии! И переданный вами 5 не является типом класса, поэтому создается скрытый временный объект, который затем передается в ссылку int && rvalue. Код, вызывающий function2 , не сможет получить доступ к этому скрытому объекту здесь, поэтому он не заметит никаких изменений.

Другая ситуация, если вы сделаете это:

SomeComplexObject o;
function2(move(o));

Вы явно запросили, чтобы o был перемещен, поэтому он будет изменен в соответствии с его спецификацией перемещения. Однако перемещение - это логически немодифицирующая операция (см. Статью).Это означает, двигаетесь вы или нет, это не должно быть видно из вызывающего кода:

SomeComplexObject o;
moveit(o); // #1
o = foo;

Если вы удалите перемещающуюся строку, поведение останется прежним, потому что оно все равно будет перезаписано. Однако это означает, что код, который использует значение o после его перемещения, является плохим , потому что он разрывает этот неявный контракт между moveit и вызывающим кодом. . Таким образом, в стандарте не указывается конкретная стоимость перемещаемого из контейнера.

10
ответ дан 3 December 2019 в 21:58
поделиться

включен утилитой


Здесь - это статья о rvalue, которую я прочитал.

Я не могу помочь вам с отдыхом, извините.

2
ответ дан 3 December 2019 в 21:58
поделиться

где определены std::move и std::forward?

std::move и std::forward объявлены в . Смотрите синопсис в начале раздела 20.3[утилиты].

При реализации семантики перемещения источник предположительно остается в неопределенном состоянии.

Это, конечно, зависит от того, как вы реализуете move-constructor и move-assignment operator. Однако, если вы хотите использовать свои объекты в стандартных контейнерах, вы должны следовать концепциям MoveConstructible и MoveAssignable, которые говорят, что объект остается действительным, но остается в неопределенном состоянии, т.е. вы определенно можете его уничтожить.

4
ответ дан 3 December 2019 в 21:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: