Копирование-исключение автоматической переменной для возврата

Мне интересно, если в C++0x "12.8 Копирование и перемещение объектов класса [class.copy] параграф 31", когда происходит copy elision, точно:

Когда выполняются определенные критерии, реализации разрешается опускать конструкцию копирования/перемещения объекта класса [...]. Это исключение операций копирования/перемещения, называемое исключением копирования, разрешено в следующих обстоятельствах [...]:

  • в операторе возврата в функции с типом возвращаемого значения класса, когда выражение является именем энергонезависимого автоматического объекта [...] с тем же типом cv-unqualified, что и возвращаемый тип функции, операция копирования/перемещения может можно опустить, встроив автоматический объект непосредственно в возвращаемое значение функции
  • [...]

И теперь мне интересно, позволяет ли это в следующем коде уклоняться от копирования

vector<string> gen(const char *fn) {
    if(fn == nullptr)  // this should prevent RVO
        return {"House", "Horse", "Hen"};
    vector<string> res;
    fillFromFile(res, fn);
    return res;  // copy elision possible?
}
int main() {
    vector<string> data = gen("users.dat");
}

Или это правило не подходит для примера, и я должен сделать его явным?

    return move(res);  // explicitly prevent copy

Обратите внимание, что моей целью в if было устранение очевидной оптимизации возвращаемого значения (RVO).

Или я здесь совершенно не на том пути? Было изменение, связанное с return и move, которое могло использовать ссылки rvalue, верно?


person towi    schedule 24.04.2011    source источник


Ответы (2)


Да, исключение копирования возможно/разрешено в обоих случаях.

Однако в терминологии компилятора эти два случая немного отличаются. return {"House", "Horse", "Hen"}; создает безымянный объект, поэтому срабатывает обычный RVO.

return res; немного сложнее, потому что вы возвращаете именованный объект, который уже был создан ранее. Эта оптимизация обычно называется NRVO (оптимизация именованного возвращаемого значения), и компиляторы реализуют ее несколько реже.

MSVC всегда реализует RVO и выполняет NRVO в сборках выпуска.

Я считаю, что последние версии GCC всегда выполняют как RVO, так и NRVO.

Между прочим, я действительно не понимаю, почему ваше «если» может иметь значение для RVO.

person jalf    schedule 24.04.2011
comment
Термин NRVO для меня новый. Но раньше это было RVOвозможно: если я создам объект один раз (задав ему имя) и заполню его разными путями, но всегда возвращаю этот заранее созданный именованный объект - тогда сработает RVO. Нет необходимости копировать и никаких новостей там, верно? - person towi; 24.04.2011
comment
Сомневаюсь во втором случае. Как упоминалось в вопросе, условие if должно предотвращать все формы RVO. - person balki; 26.04.2011
comment
@балки: почему? Я не вижу в цитируемом тексте ничего, что запрещало бы это. - person jalf; 26.04.2011

Да, у компилятора есть специальные инструкции для обработки res как значения r в этом контексте, и res будет перемещено в data. Конечно, компилятор все равно мог бы легко применить здесь RVO/NRVO, потому что он может статически определить, что вы никогда не вызываете функцию с nullptr, и, кроме того, функция может быть тривиально преобразована, чтобы можно было применить RVO/NRVO, даже если бы это было невозможно. не может быть доказано, и, наконец, это даже не предотвращает RVO/NRVO, поскольку результат все еще может быть сконструирован.

person Puppy    schedule 24.04.2011
comment
Хорошо, main не был хорошим примером. Очевидно, что компилятор может удалить половину моей программы :-) Но предоставление двух путей с двумя совершенно разными возвратами... тогда это ново, потому что return res может рассматривать res как rval-ref, и, таким образом, , применить ход? - person towi; 24.04.2011