Para una clase Foo, ¿hay alguna manera de no permitir su construcción sin darle un nombre?
Por ejemplo:
Foo("hi");
¿Y solo permitirlo si le da un nombre, como el siguiente?
Foo my_foo("hi");
La vida útil del primero es solo la declaración y el segundo es el bloque adjunto. En mi caso de uso, Foo
está midiendo el tiempo entre el constructor y el destructor. Como nunca me refiero a la variable local, a menudo me olvido de ponerla y accidentalmente cambio la duración. En su lugar, me gustaría obtener un error de tiempo de compilación.
return std::string("Foo");
)Respuestas:
Otra solución basada en macros:
La declaración se
Foo("hi");
expande aclass Foo("hi");
, que está mal formada; pero seFoo a("hi")
expande a loclass Foo a("hi")
que es correcto.Esto tiene la ventaja de que es compatible tanto con el código fuente como con el código binario existente (correcto). (Esta afirmación no es del todo correcta; consulte el comentario de Johannes Schaub y la discusión subsiguiente a continuación: "¿Cómo puede saber que es compatible con el código fuente existente? Su amigo incluye su encabezado y tiene void f () {int Foo = 0;} que anteriormente se compilaba bien y ahora se compila mal. Además, cada línea que define una función miembro de la clase Foo falla: void class Foo :: bar () {} " )
fuente
void f() { int Foo = 0; }
previamente compilado bien y ahora compila mal. Además, cada línea que define una función miembro de la clase Foo falla:void class Foo::bar() {}
.Foo("Hi")
dentro de Foo.cpp ahoraclass Foo("hi");
está bien para compilar.¿Qué tal un pequeño truco?
fuente
Foo a("hi");
(sinclass
) también sería un error.Foo
porque tiene prioridad sobre una clase.Foo::Foo("hi")
está permitido en C ++.Haz que el constructor sea privado pero dale a la clase un método de creación .
fuente
Foo::create();
sobreFoo const & x = Foo::create();
std::common_type<Foo>::type()
y obtienes un archivo temporal. O inclusotypedef Foo bar; bar()
.std::common_type<Foo>::type()
por error. Dejar fuera elFoo const & x = ...
por accidente es totalmente creíble.Este no da como resultado un error del compilador, sino un error de tiempo de ejecución. En lugar de medir un tiempo incorrecto, obtiene una excepción que también puede ser aceptable.
Cualquier constructor que desee proteger necesita un argumento predeterminado en el que
set(guard)
se llama.Las caracteristicas son:
El caso de
f2
,f3
y el regreso de"hello"
no estar querido. Para evitar que se arroje, puede permitir que la fuente de una copia sea temporal, restableciendo elguard
para protegernos ahora en lugar de la fuente de la copia. Ahora también verá por qué usamos los indicadores anteriores: nos permite ser flexibles.Las características para
f2
,f3
y porreturn "hello"
ahora son siempre// OK
.fuente
Foo f = "hello"; // may throw
Esto es suficiente para asustarme para que nunca use este código.explicit
y luego dicho código no se compila más. el objetivo era prohibir lo temporal, y lo hace. si tiene miedo, puede hacer que no se lance estableciendo la fuente de una copia en el constructor de copia o movimiento como no temporal. entonces solo el objeto final de varias copias puede arrojar si aún termina como temporal.Foo
objeto también es temporal, y su duración termina en la misma expresión que el argumento predeterminado, entonces elFoo
dtor del objeto se invocará antes que el dtor del argumento predeterminado, porque el primero se creó después del segundo.Foo(...);
yFoo foo(...);
desde dentro delFoo
.Hace unos años escribí un parche para el compilador GNU C ++ que agrega una nueva opción de advertencia para esa situación. Esto se rastrea en un artículo de Bugzilla .
Desafortunadamente, GCC Bugzilla es un cementerio donde las sugerencias de características bien consideradas incluidas en el parche van a morir. :)
Esto fue motivado por el deseo de detectar exactamente el tipo de errores que son el tema de esta pregunta en un código que usa objetos locales como dispositivos para bloquear y desbloquear, medir el tiempo de ejecución, etc.
fuente
Tal como está, con su implementación, no puede hacer esto, pero puede usar esta regla a su favor:
Puede mover el código de la clase a una función independiente que toma un parámetro de referencia no constante. Si lo hace, obtendrá un error del compilador si un temporal intenta vincularse a la referencia no constante.
Muestra de código
Salida
fuente
x
es un objeto con nombre, por lo que no está claro si realmente queremos prohibirlo. Si el constructor que habría utilizado es explícito, muchas personas lo harían instintivamenteFoo f = Foo("hello");
. Creo que se enojarían si fallara. Mi solución inicialmente lo rechazó (y casos muy similares) con una excepción / afirmación-falla y alguien se quejó.Simplemente no tiene un constructor predeterminado y requiere una referencia a una instancia en cada constructor.
fuente
S(selfRef, a);
. : /S(SelfRef, S const& s) { assert(&s == this); }
, si se acepta un error en tiempo de ejecución.No, me temo que esto no es posible. Pero podría obtener el mismo efecto creando una macro.
Con esto en su lugar, puede escribir FOO (x) en lugar de Foo my_foo (x).
fuente
Foo();
.class Do_not_use_this_class_directly_Only_use_it_via_the_FOO_macro;
Dado que el objetivo principal es prevenir errores, considere esto:
De esa manera no puede olvidarse de nombrar la variable y no puede olvidarse de escribir
struct
. Detallado, pero seguro.fuente
Declare un constructor paramétrico como explícito y nadie jamás creará un objeto de esa clase sin querer.
Por ejemplo
solo se puede usar de esta manera
pero nunca de esta manera (a través de un temporal en la pila)
Consulte también: Alexandrescu, Estándares de codificación C ++, artículo 40.
fuente
fun(Foo("text"));
.