Para uma classe Foo, existe uma maneira de proibir sua construção sem dar a ela um nome?
Por exemplo:
Foo("hi");
E só permite se você der um nome, como o seguinte?
Foo my_foo("hi");
O tempo de vida do primeiro é apenas a instrução e o segundo é o bloco envolvente. No meu caso de uso, Foo
é medir o tempo entre o construtor e o destruidor. Como nunca me refiro à variável local, frequentemente esqueço de inseri-la e, acidentalmente, mudo o tempo de vida. Eu gostaria de obter um erro de tempo de compilação.
return std::string("Foo");
)Respostas:
Outra solução baseada em macro:
A declaração se
Foo("hi");
expande paraclass Foo("hi");
, que está malformada; mas seFoo a("hi")
expande paraclass Foo a("hi")
, o que é correto.Isso tem a vantagem de ser compatível com o código-fonte e o binário com o código existente (correto). (Esta afirmação não está totalmente correta - consulte o comentário de Johannes Schaub e a discussão subsequente abaixo: "Como você pode saber se o código-fonte é compatível com o código existente? Seu amigo inclui seu cabeçalho e tem void f () {int Foo = 0;} que anteriormente compilava bem e agora compila incorretamente! Além disso, cada linha que define uma função de membro da classe Foo falha: void class Foo :: bar () {} " )
fonte
void f() { int Foo = 0; }
que compilou corretamente e agora compila incorretamente! Além disso, cada linha que define uma função membro da classe Foo falha:void class Foo::bar() {}
.Foo("Hi")
dentro de Foo.cpp agoraclass Foo("hi");
OK para compilar.Que tal um pequeno hack
fonte
Foo a("hi");
(semclass
) seria um erro também.Foo
porque tem precedência sobre uma classe.Foo::Foo("hi")
é permitido em C ++.Torne o construtor privado, mas dê à classe um método de criação .
fonte
Foo::create();
sobreFoo const & x = Foo::create();
std::common_type<Foo>::type()
e obter um temporário. Ou mesmotypedef Foo bar; bar()
.std::common_type<Foo>::type()
por engano. Deixar de fora oFoo const & x = ...
acidente é totalmente verossímil.Este não resulta em um erro do compilador, mas um erro de tempo de execução. Em vez de medir um tempo errado, você obtém uma exceção que também pode ser aceitável.
Qualquer construtor que você deseja proteger precisa de um argumento padrão no qual
set(guard)
é chamado.As características são:
O caso de
f2
,f3
eo retorno de"hello"
não ser querido. Para evitar o lançamento, você pode permitir que a origem de uma cópia seja temporária, redefinindo oguard
para agora nos proteger em vez da origem da cópia. Agora você também vê porque usamos as dicas acima - isso nos permite ser flexíveis.As características para
f2
,f3
e parareturn "hello"
agora são sempre// OK
.fonte
Foo f = "hello"; // may throw
Isso é o suficiente para me assustar e nunca mais usar esse código.explicit
e então esse código não compila mais. o objetivo era proibir o temporário, e é o que acontece. se estiver com medo, você pode fazer com que ele não seja jogado definindo a origem de uma cópia no construtor de cópia ou movimento como não temporário. então, apenas o objeto final de várias cópias pode ser lançado, se ainda assim terminar como temporário.Foo
objeto também for temporário e seu tempo de vida terminar na mesma expressão do argumento padrão, oFoo
dtor do objeto será invocado antes do dtor do argumento padrão, porque o primeiro foi criado depois do último.Foo(...);
eFoo foo(...);
de dentro doFoo
.Alguns anos atrás, escrevi um patch para o compilador GNU C ++ que adiciona uma nova opção de aviso para essa situação. Isso é rastreado em um item do Bugzilla .
Infelizmente, o GCC Bugzilla é um cemitério onde sugestões de recursos bem consideradas com patch incluído vão morrer. :)
Isso foi motivado pelo desejo de detectar exatamente o tipo de bugs que são o assunto desta questão no código que usa objetos locais como dispositivos para bloquear e desbloquear, medir o tempo de execução e assim por diante.
fonte
Como está, com sua implementação, você não pode fazer isso, mas pode usar esta regra a seu favor:
Você pode mover o código da classe para uma função independente que usa um parâmetro de referência não const. Se você fizer isso, obterá um erro do compilador se um temporário tentar se vincular à referência não const.
Amostra de Código
Resultado
fonte
x
é um objeto nomeado, então não está claro se realmente queremos proibi-lo. Se o construtor que você usaria for explícito, as pessoas o fariam instintivamenteFoo f = Foo("hello");
. Acho que eles ficariam com raiva se falhasse. Minha solução inicialmente rejeitou (e casos muito semelhantes) com uma exceção / falha de afirmação e alguém reclamou.Simplesmente não tem um construtor padrão e exige uma referência a uma instância em cada construtor.
fonte
S(selfRef, a);
. : /S(SelfRef, S const& s) { assert(&s == this); }
, se um erro de tempo de execução for aceitável.Não, infelizmente isso não é possível. Mas você pode obter o mesmo efeito criando uma macro.
Com isso no lugar, você pode simplesmente escrever FOO (x) em vez de Foo my_foo (x).
fonte
Foo();
.class Do_not_use_this_class_directly_Only_use_it_via_the_FOO_macro;
Como o objetivo principal é evitar bugs, considere o seguinte:
Dessa forma, você não pode esquecer de nomear a variável e não pode se esquecer de escrever
struct
. Detalhado, mas seguro.fonte
Declare um construtor paramétrico como explícito e ninguém jamais criará um objeto dessa classe acidentalmente.
Por exemplo
só pode ser usado desta forma
mas nunca desta forma (através de um temporário na pilha)
Veja também: Alexandrescu, C ++ Coding Standards, Item 40.
fonte
fun(Foo("text"));
.