RVO forçar erro de compilação na falha
Muitas discussões aqui sobre quando o RVO pode ser feito, mas não muito sobre quando realmente é feito. Como mencionado, a RVO não pode ser garantida de acordo com o Padrão, masExiste uma maneira de garantir que a otimização de RVO seja bem-sucedida ou que o código correspondente não seja compilado?
Até agora eu consegui parcialmente fazer o código emitir erros de link quando o RVO falha. Para isso eu declaro os construtores de cópia sem defini-los. Obviamente, isso não é robusto nem viável nos casos não raros em que preciso implementar um ou ambos os construtores de cópia, ou seja,x(x&&)
ex(x const&)
.
Isso me leva à segunda pergunta:Por que os escritores do compilador escolheram ativar o RVO quando os construtores de cópia definidos pelo usuário estão em vigor, mas não quando apenas os construtores de cópia padrão estão presentes?
Terceira pergunta:Existe alguma outra maneira de ativar o RVO para estruturas de dados simples?
Última pergunta (promessa):Você conhece algum compilador que faz com que meu código de teste se comporte com outro, então observei com o gcc e o clang?
Aqui está um exemplo de código para o gcc 4.6, gcc 4.8 e clang 3.3 que mostra o problema. O comportamento não depende de configurações gerais de otimização ou depuração. É claro opção--no-elide-constructors
faz o que diz, ou seja, desliga o 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;
}
Saída:
original x address0x7fff8ef01dff
copy of x address0x7fff8ef01e2e
original y address0x7fff8ef01e2f
copy of y address0x7fff8ef01e2f
O RVO também parece não funcionar com estruturas de dados simples:
#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;
}
Saída:
original x address0x7fffe7bb2320
copy of x address0x7fffe7bb2350
ATUALIZAR: Observe que algumas otimizações são muito facilmente confundidas com o RVO. Ajudantes de construtores comomake_x
são um exemplo. Vejoeste exemplo onde a otimização é realmente aplicada pelo padrão.