Это не совсем полный ответ, но он может дать подсказку.Как я и подозревал, в значениях fast
и slow
есть небольшая разница, которая, вероятно, отправляет компилятор по разным путям.Это можно увидеть, если вы сделаете конструктор копирования закрытым.
https://godbolt.org/z/FMIRe3
#include <array>
class C
{
std::array<char, 7> a{};
public:
C(){}
private:
C(const C & c){}
};
// Compiles
C slow()
{
return {};
}
// Does not compile
C fast()
{
C c;
return c;
}
Даже с копией ellision fast
все еще требует, чтобы конструктор копирования находился там, где slow
возвращает initialization list
, который явно создает возвращаемое значение вызывающей стороной.Они могут или не могут в конечном итоге делать то же самое, но я считаю, что компилятор должен сделать некоторые хитрости, чтобы определить, так ли это.
Существует подробное сообщение в блоге, которое дает некоторую интересную информацию по этой теме
https://akrzemi1.wordpress.com/2018/05/16/rvalues-redefined/
Однако поведение изменилось в C ++ 17
, тогда как
#include <array>
class C
{
std::array<char, 7> a{};
public:
C(){}
private:
C(const C & c){}
};
C slow()
{
return {};
}
C fast()
{
return C();
}
fast
не скомпилируетсяв C ++ 11 теперь он компилируется в C ++ 17
https://godbolt.org/z/JG2PkD
Причина в том, что значение return C()
меняется от возврата временного к явному конструированию объекта вкадр звонящего.
Так что теперь в C ++ 17 есть большая разница между
C fast(){
C c;
return c;
}
и
C fast(){
return C();
}
, потому что во втором вы надеваетедаже не нужен конструктор копирования или перемещения, чтобы быть доступным.
https://godbolt.org/z/i2eZnf
Определенно не C ++ 101