Использование ссылок rvalue в C++11

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

std::vector<int> &&fill_list() {
  std::vector<int> res;
  ... do something to fill res ...
  return res;
}

int main(int argc, char **argv) {
  std::vector<int> myvec = fill_list();
  return 0;
}

но это не работает, я получаю следующую ошибку:

error: invalid initialization of reference of type 'std::vector<int>&&' from expression of type 'std::vector<int>'

Итак, в целом, как правильно это сделать? Я не думаю, что я получаю ссылки rvalue только пока.

4 ответов


вы, похоже, смущены тем, что такое ссылка rvalue и как она относится к семантике перемещения.

первое дело первое:&& не означает двигаться. Это не что иное, как специальный ссылочный тип. Это еще ссылка. Это не ценность, это не двигался значение; это ссылка значение. Это означает, что он имеет все ограничения ссылочного типа. Примечательно, что он должен ссылаться на значение, которое существует. Таким образом, возврат ссылки на болтающееся r-значение не лучше, чем возврат ссылки на болтающееся l-значение.

"перемещение" - это процесс, когда один объект претендует на владение содержимым другого объекта. Ссылки на значения R облегчать переместить семантику, но просто имея && ничего не значит переехал. Движение только происходит, когда вызывается конструктор перемещения (или оператор назначения перемещения); если только один из них две вещи называются, никакого движения не произошло.

если вы хотите переместить содержимое std::vector из вашей функции пользователю, вы просто делаете это:

std::vector<int> fill_list() {
  std::vector<int> res;
  ... do something to fill res ...
  return res;
}

учитывая это использование fill_list():

std::vector<int> myvec = fill_list();

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

если бы у вас было это:

std::vector<int> myvec;
myvec = fill_list();

опять же, это будет переехали в. Никакого копирования.

C++11 знает, когда безопасно неявно перемещать вещи. Возврат значения по значению, а не по ссылке или чему-то всегда является безопасным временем для перемещения. Поэтому он будет двигаться.


для обсуждения ссылок rvalue вы можете прочитать, что Бьярне Страуструп, автор C++, должен сказать о них здесь:

http://www2.research.att.com / ~bs / C++0xFAQ.html#rval

обращаясь к вашему конкретному примеру, короткий ответ заключается в том, что из-за Именованная Оптимизация Возвращаемого Значения - что является де-факто стандартной функцией компилятора C++ даже до C++11-Если вы просто вернетесь по значению, компилятор свяжет res и myvec эффективно, как вы хотите:

std::vector<int> fill_list() {
    std::vector<int> res;
    ... do something to fill res ...
    cout << &res << endl; // PRINT POINTER
    return res;
}

int main(int argc, char **argv) {
     std::vector<int> myvec = fill_list();
     cout << &myvec << endl; // PRINT POINTER
     return 0;
}

две строки "PRINT POINTER" будут печатать тот же указатель в обоих случаях.

вектор myvec и вектор res в приведенном выше будет тем же вектором с тем же хранилищем. Конструктор копирования или конструктор перемещения вызываться не будет. В некотором смысле res и myvec будут двумя псевдонимами одного и того же объекта.

Это даже лучше, чем использование конструктора перемещения. myvec построен и " заполнен" на месте.

компилятор достигает этого, компилируя функцию в режиме "inplace", накладывая немедленную позицию стека в кадре стека вызывающих абонентов с локальной переменной результата вызываемых и просто оставляя ее там после возвращения вызываемого.

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

http://en.wikipedia.org/wiki/Return_value_optimization

в случае, если вы назначали результат fill_list в контексте, отличном от конструктора, чем в качестве результата возврата по значению в xvalue (сокращение от" expiring " value, тип rvalue), оператор назначения перемещения целевой переменной будет отдан предпочтение, если он доступен.


оператор return является ошибкой, потому что вы atempt для привязки ссылки rvalue (тип возврата) к lvalue (вектор res). Ссылка rvalue может быть привязана только к rvalue.

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

Если вы хотите, чтобы избежать конструкция копирования во время инструкции return, просто используя не ссылочный тип, может уже работать из-за функции под названием copy elision. вектор res в вашей функции fill_list может быть непосредственно построен в вектор myvec в вашей основной функции, поэтому конструкция копирования или перемещения не вызывается вообще. Но эта функция разрешена стандартом не требуется, некоторая конструкция копирования не опущена в некотором компиляторе.


Если вы просто удалить && из вашей функции он должен работать, но это не будет ссылкой. fill_list () создаст вектор и вернет его. Во время возврата будет создан новый вектор. Первый вектор, который был создан внутри fill_list (), будет скопирован в новый вектор, а затем будет уничтожен. Это работа конструктора копирования.