Проблема показана в фрагменте кода ниже. Функция, объявленная с типом автоматического возврата, которая возвращает именованный объект класса, имеет в своем теле дополнительную конструкцию копирования. Если возврат перекодируется для возврата безымянного временного объекта, происходит исключение. Если функция перекодируется так, чтобы явно возвращать экземпляр класса (вместо auto), происходит исключение.
Если структура A не имеет параметра шаблона, то также генерируется полностью исключенный код.
Проблема показывает, все ли является noException или разрешено ли встраивание (NOINLINE позволяет увидеть проблему в Godbolt без необходимости выполнения кода).
Код: Выделить всё
// compiled with -O2 -std=c++17
#if defined(_MSC_VER) && !defined(__clang__)
#define NOINLINE __declspec(noinline)
#else
#define NOINLINE __attribute__((noinline))
#endif
template
struct A {
int data = 0;
NOINLINE explicit A(int data_) noexcept : data(data_) { }
NOINLINE ~A() noexcept { }
NOINLINE A(const A& other) noexcept : data(other.data) { }
};
template
NOINLINE auto return_auto_A_nrvo(const A& a) noexcept {
/* clang 6.0 thru 8.0 doesn't elide copy of 'result':
gcc and msvc elide the copy as expected.
mov r14, rsp
mov rdi, r14
call A::A(A const&)
mov rdi, rbx
mov rsi, r14
call A::A(A const&)
mov rdi, r14
call A::~A() [base object destructor]
* return A(a); is fully optimized
*/
A result(a);
return result;
}
template
NOINLINE A return_A_nrvo(const A& a) noexcept {
// NRVO with explicit return type: fully optimized
A result(a);
return result;
}
template
NOINLINE auto return_auto_A_rvo(const A& a) noexcept {
// RVO: fully optimized
return A(a);
}
NOINLINE int main() {
auto a1 = A(42);
auto a2 = return_auto_A_nrvo(a1);
auto a3 = return_A_nrvo(a1);
auto a4 = return_auto_A_rvo(a1);
return a2.data + a3.data + a4.data;
}
Эта ссылка Godbolt показывает код, сгенерированный GCC, clang и msvc: https://www.godbolt.org/z/FDAvQO.
Возможно, это просто ошибка/упущенная возможность оптимизации, которую clang упускает, а бренды G и M - нет. Если это так, я постараюсь найти подходящее место, чтобы опубликовать это, чтобы ребята из clang могли это исправить. Но я чувствую, что здесь может быть что-то более глубокое, например, фундаментальное различие между возвратом auto и возвратом объекта шаблонного класса. Я считаю, что C++17 гарантирует, что unnamed-RVO всегда будет возникать, но использование именованного-RVO, как в моем случае, не гарантировано - я хотел бы понять, почему это так (и почему это применимо здесь).
Подробнее здесь: https://stackoverflow.com/questions/562 ... eturn-type
Мобильная версия