RVO/NRVO 2/2.
Как понять, убиваете ли вы NRVO? Можно сделать такой же класс, который выводит операции со своими конструкторами. В случае некоторых компиляторов (clang) можно ещё одним способом, о котором рассказали в комментариях в посте про этапы компиляции. На этапе семантического разбора компилятор производит всякие проверки на корректность типов в выражениях и подобном. Например у clang к основному условия про совпадение типов для NRVO ещё проверяется, правда ли, что во всех
return возвращается одна и та же локальная переменная. Посмотрим на пример (gb): void func1(std::optional<std::string>&);
std::string get_s();
void func2(std::string&);
std::optional<std::string> f(bool flag) {
std::optional<std::string> null;
func1(null);
if (flag) return null; // (1)
std::string s = get_s();
func2(s);
return s; // (2)
}Во-первых, корректно ли мы в (2) возвращаем
s? Ответ нет. Лучше мувнуть. Потому что в данном случае будет вызван конструктор std::optional<std::string>(const std::string&) для RVO. Если же мувать, получим std::optional<std::string>(std::string&&). Хотя с C++20 мувать уже необязательно. Во-вторых, случится ли NRVO в (1)? В данном случае нет, т.к. эта локальная переменная не возвращается во всех
return. Однако если начать возвращать её и во втором return, то в AST можно будет увидеть строчку:`-VarDecl <col:5, col:32> col:32 used null 'std::optional<std::string>':'std::optional<std::basic_string<char>>' nrvo callinit destroyedС пометкой
nrvo. Получается, можно сэмулировать структуру вашего кода и посмотреть на AST. Оч прикона кмк. Если подумать, почему же одна и та же локальная переменная во всех
return важна, то всё становится понятно: компилятор уверен, что эту переменную в памяти можно создавать сразу в return slot результата функции. В обратном случае это неверно, так что переменная null копируется.Круто🤭
Если вы об этом паритесь, общие рекомендации такие:
1. Пытаться возвращать prvalue.
2. Возвращать одну и ту же локальную переменную во всех
return. Аналогичные правила примерно применимы и к исключениям, о которых два других пункта в стандарте. Но, честно говоря, если вы пытаетесь оптимизировать и так не очень производительные исключения, что-то пошло не так : )
Тут можно посмотреть хороший небольшой доклад про то, как это всё работает (с чуть бОльшим количеством технических деталей) и с примерами, когда это не работает.
Ещё можно посмотреть вот это бомбическое видео.