Когда компилятор С++ 11 сделает RVO и NRVO опережающими семантику перемещения и привязку ссылки ссылки?

Рассмотрим случай, когда объекты "целого" с включенной семантикой перемещения возвращаются из функций, например, с помощью std::basic_string<>:

std::wstring build_report() const
{
    std::wstring report;
    ...

    return report;
}

Могу ли я тогда, как можно ожидать, сделать "лучший" выбор, использовать ли возвращенную строку с семантикой перемещения, как в

const std::wstring report(std::move(build_report()));

или если я полагаюсь на (N) RVO, чтобы иметь место с

const std::wstring report(build_report());

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

const std::wstring& report(build_report());

Какая схема состоит в том, чтобы сделать детерминированный выбор этих параметров, если они есть?

РЕДАКТИРОВАТЬ 1: Обратите внимание, что использование std::wstring выше - это просто пример типа семантики с поддержкой перемещения. Он также будет заменен на ваш arbitrary_large_structure.: -)

РЕДАКТИРОВАТЬ 2: Я проверил сгенерированную сборку при запуске версии с оптимизацией скорости в VS 2010 следующего вида:

std::wstring build_report(const std::wstring& title, const std::wstring& content)
{
    std::wstring report;
    report.append(title);
    report.append(content);

    return report;
}

const std::wstring title1(L"title1");
const std::wstring content1(L"content1");

const std::wstring title2(L"title2");
const std::wstring content2(L"content2");

const std::wstring title3(L"title3");
const std::wstring content3(L"content3");

int _tmain(int argc, _TCHAR* argv[])
{
    const std::wstring  report1(std::move(build_report(title1, content1)));
    const std::wstring  report2(build_report(title2, content2));
    const std::wstring& report3(build_report(title3, content3));

    ...

    return 0;
}

Два наиболее интересных результата:

  • Явный вызов std::move для report1 для использования конструктора перемещения тройки счетчика команд.
  • Как отметил Джеймс Макнеллис в его ответе ниже, report2 и report3 действительно генерируют идентичную сборку с инструкциями в 3 раза меньше, чем явным вызовом std::move.

Ответы

Ответ 1

std::move(build_report()) совершенно не нужно: build_report() уже является выражением rvalue (это вызов функции, возвращающей объект по значению), поэтому конструктор перемещения std::wstring будет использоваться, если он имеет один (он делает).

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

Не должно быть никакого функционального различия между объявлением report в качестве объекта или в качестве ссылки на const; в обоих случаях вы получаете объект (либо названный объект report, либо неназванный объект, к которому может привязываться ссылка report).

Ответ 2

Я не уверен, стандартизирован ли это (как говорит Никол, все оптимизация зависит от компилятора), но я слышал STL поговорите об этом и (по крайней мере, в MSVC), RVO происходит раньше всего. Поэтому, если есть шанс применить RVO, то это произойдет без каких-либо действий с вашей стороны. Во-вторых, когда вы возвращаете временное, вам не нужно писать std::move (я думаю, что это действительно стандарт), так как возвращаемое значение будет неявно рассматриваться как rvalue.

Результат: не переусердствуйте с компилятором, просто напишите наиболее естественный код, и он даст вам наилучший результат.

Ответ 3

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

Нет никого, и никогда не будет.

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

Самое большее, что вы в конечном итоге получите, - это общее эвристическое сообщество, где люди говорят, "для большинства компиляторов X, похоже, работает быстрее". Но об этом. И это займёт годы, когда компиляторы получают скорость с С++ 0x, а реализации - зрелыми.