Błąd kompilacji RVO przy awarii
Wiele dyskusji na temat tego, kiedy można zrobić RVO, ale niewiele o tym, kiedy to się robi. Jak już wspomniano, czasy RVO nie mogą być zagwarantowane zgodnie z normą, aleczy istnieje sposób na zagwarantowanie, że optymalizacja RVO powiedzie się lub odpowiedni kod nie zostanie skompilowany?
Do tej pory udało mi się częściowo wprowadzić błędy linku w przypadku niepowodzenia RVO. W tym celu deklaruję konstruktorów kopii bez ich definiowania. Oczywiście nie jest to ani solidne, ani wykonalne w rzadkich przypadkach, w których muszę zaimplementować jednego lub oba konstruktory kopii, tj.x(x&&)
ix(x const&)
.
To prowadzi mnie do drugiego pytania:Dlaczego twórcy kompilatora wybrali włączenie RVO, gdy zdefiniowano konstruktory kopiowania zdefiniowane przez użytkownika, ale nie wtedy, gdy obecne są tylko domyślne konstruktory kopiowania?
Trzecie pytanie:Czy jest jakiś inny sposób włączenia RVO dla prostych struktur danych?
Ostatnie pytanie (obietnica):Czy znasz jakiś kompilator, który powoduje, że mój kod testowy zachowuje się inaczej niż w przypadku gcc i clang?
Oto przykładowy kod gcc 4.6, gcc 4.8 i clang 3.3, który pokazuje problem. Zachowanie nie zależy od ogólnych ustawień optymalizacji lub debugowania. Oczywiście opcja--no-elide-constructors
robi to, co mówi, tzn. wyłącza RVO.
#include <iostream>
using namespace std;
struct x
{
x () { cout << "original x address" << this << endl; }
};
x make_x ()
{
return x();
}
struct y
{
y () { cout << "original y address" << this << endl; }
// Any of the next two constructors will enable RVO even if only
// declared but not defined. Default constructors will not do!
y(y const & rhs);
y(y && rhs);
};
y make_y ()
{
return y();
}
int main ()
{
auto x1 = make_x();
cout << "copy of x address" << &x1 << endl;
auto y1 = make_y();
cout << "copy of y address" << &y1 << endl;
}
Wydajność:
original x address0x7fff8ef01dff
copy of x address0x7fff8ef01e2e
original y address0x7fff8ef01e2f
copy of y address0x7fff8ef01e2f
RVO wydaje się również nie działać ze zwykłymi strukturami danych:
#include <iostream>
using namespace std;
struct x
{
int a;
};
x make_x ()
{
x tmp;
cout << "original x address" << &tmp << endl;
return tmp;
}
int main ()
{
auto x1 = make_x();
cout << "copy of x address" << &x1 << endl;
}
Wydajność:
original x address0x7fffe7bb2320
copy of x address0x7fffe7bb2350
AKTUALIZACJA: Zauważ, że niektóre optymalizacje są bardzo łatwo mylone z RVO. Pomocnicy konstruktorzy lubiąmake_x
są przykładem. Widziećten przykład gdzie optymalizacja jest faktycznie egzekwowana przez standard.