Global const string& cheira mal para mim, é realmente seguro?
Pergunta
Estou revisando o código de um colega, e vejo que ele possui diversas constantes definidas no escopo global como:
const string& SomeConstant = "This is some constant text";
Pessoalmente, isso me cheira mal porque a referência se refere ao que presumo ser um objeto "anônimo" construído a partir de um determinado array de caracteres.
Sintaticamente, é legal (pelo menos no VC++ 7) e parece funcionar, mas na verdade prefiro que ele remova o & para que não haja ambigüidade quanto ao que está fazendo.
Então, isso é VERDADEIRAMENTE seguro e legal e estou obcecado?O objeto temporário que está sendo construído tem vida útil garantida?Sempre presumi que objetos anônimos usados dessa maneira eram destruídos após o uso...
Portanto, minha pergunta também poderia ser generalizada para a vida útil do objeto anônimo.O padrão determina o tempo de vida de um objeto anônimo?Teria o mesmo tempo de vida de qualquer outro objeto no mesmo escopo?Ou é dado apenas o tempo de vida da expressão?
Além disso, ao fazer isso como local, obviamente o escopo é diferente:
class A
{
string _str;
public:
A(const string& str) :
_str(str)
{
cout << "Constructing A(" << _str << ")" << endl;
}
~A()
{
cout << "Destructing A(" << _str << ")" << endl;
}
};
void TestFun()
{
A("Outer");
cout << "Hi" << endl;
}
Mostra:
Construindo A (Externo);Destruindo A (Externo);Oi
Solução
É completamente legal.Não será destruído até que o programa termine.
EDITAR: Sim, é garantido:
"Todos os objetos que não têm duração dinâmica de armazenamento, não têm duração de armazenamento de roscas e não são locais têm duração de armazenamento estático.O armazenamento desses objetos deve durar a duração do programa (3.6.2, 3.6.3). "
-- Rascunho de trabalho de 2008, padrão para linguagem de programação C++, § 3.7.1 pág.63
Como observou Martin, esta não é a resposta completa.O projeto padrão de notas adicionais (§ 12.2, p.250-1):
"Os temporários do tipo de classe são criados em vários contextos:Vinculando um rvalue a uma referência (8.5.3) [...] Mesmo quando a criação do objeto temporário é evitada (12,8), todas as restrições semânticas devem ser respeitadas como se o objeto temporário tivesse sido criado....] Objetos temporários são destruídos como o último passo para avaliar a expressão total (1.9) que (lexicamente) contém o ponto em que foram criados....] Existem dois contextos em que os temporários são destruídos em um ponto diferente do que o final da expressão total....] O segundo contexto é quando uma referência está vinculada a um temporário.O temporário ao qual a referência é ligada ou o temporário que é o objeto completo de um subobjetivo ao qual a referência é ligada persiste durante a vida útil da referência, exceto conforme especificado abaixo. "
Testei em g++ se isso faz você se sentir melhor.;)
Outras dicas
Sim, é válido e legal.
const string& SomeConstant = "This is some constant text";
// Is equivalent too:
const string& SomeConstant = std::string("This is some constant text");
Assim você está criando um objeto temporário.
Este objeto temporário está vinculado a um const& e, portanto, tem seu tempo de vida estendido até o tempo de vida da variável ao qual está vinculado (ou seja, mais longo que a expressão na qual foi criado).
Isto é garantido pela norma.
Observação:
Embora seja legal.Eu não usaria isso.A solução mais fácil seria convertê-lo em const std::string.
Uso:
Nesta situação, como a variável está no escopo global, ela é válida para toda a extensão do programa.Portanto, ele pode ser usado assim que a execução entrar em main() e não deve ser acessado após a execução sair de main().
Embora tecnicamente possa estar disponível antes disso, seu uso em construtores/destruidores de objetos globais deve ser moderado com o problema conhecido da ordem de inicialização de variáveis globais.
Pensamentos extras:
Este, por outro lado, não sofrerá com o problema:
char const* SomeConstant = "This is some constant text";
E pode ser usado em qualquer momento.Apenas um pensamento.
Pode ser legal, mas ainda assim feio.Deixe de fora a referência!
const string SomeConstant = "This is some constant text";
É tão legal quanto feio.
É legal estender uma variável temporária com um const
referência, esta é usada por Alexandrescu EscopoGaurd veja esta excelente explicação de Herb Sutter chamada Um candidato para o "Mais importante const
".
Dito isto, este caso específico é um abuso deste recurso do C++ e a referência deve ser removida deixando um claro const string
.
Declará-lo como const (o que significa que não pode ser alterado) e depois torná-lo uma referência, o que implica que alguém pode alterá-lo, parece, no mínimo, uma má forma.Além disso, como tenho certeza que você entende, variáveis globais são RUINS e raramente necessárias.
Ok, pessoal, corrijam-me se eu estiver errado, mas aqui estão minhas conclusões ouvindo todas as suas excelentes respostas:
A) é sintaticamente e logicamente legal, o & estende a vida útil do temporário/anônimo além do nível de expressão até a vida da referência.Eu verifiquei isso no VC++7 com:
class A {
public: A() { cout << "constructing A" << endl; }
public: ~A() { cout << "destructing A" << endl; }
};
void Foo()
{
A();
cout << "Foo" << endl;
}
void Bar()
{
const A& someA = A();
cout << "Bar" << endl;
}
int main()
{
Foo(); // outputs constructing A, destructing A, Foo
Bar(); // outputs constructing A, Bar, destructing A
return 0;
}
B) Embora seja legal, pode levar a alguma confusão quanto ao tempo de vida real e a referência nestes casos não lhe dá nenhum benefício de declará-la como não-referência, portanto, a referência provavelmente deve ser evitada e pode até ser um espaço extra .Como não há nenhum benefício nisso, é uma ofuscação desnecessária.
Obrigado por todas as respostas, foi uma discussão muito interessante.Então, resumindo:Sim, é sintaticamente legal, não, não é tecnicamente perigoso, pois a vida útil é estendida, mas não acrescenta nada e pode adicionar custos e confusão, então por que se preocupar?
Parece certo?