Estoy tratando de implementar una clase para realizar un seguimiento de las actuaciones de un código multiproceso y estoy corriendo en el problema siguiente.
El rastreador debe tener una única instancia, por lo tanto el patrón singleton. Con el fin de contar con la creación de objetos y la ejecución de la función al otro lado de todos los hilos, pensé que el uso de miembro atómica sería una idea buena. Pero me parece que no puede encontrar una aplicación correcta.
Se trata de un código mínimo de lo que la varita que hacer:
#include <atomic>
#include <iostream>
class Counter
{
public:
static Counter& instance()
{
return instance_;
};
void increment()
{
counter_++;
};
private:
Counter ()
{
std::cout << "ctor counter" << std::endl;
};
~Counter ()
{
std::cout << "counter_: " << counter_ << std::endl;
std::cout << "dtor counter" << std::endl;
}
static Counter instance_;
std::atomic<int> counter_{0};
//int counter_ = 0;
};
Counter Counter::instance_ = Counter();
int main(void)
{
Counter::instance().increment();
Counter::instance().increment();
Counter::instance().increment();
return 0;
}
Si la counter_
variable es una int
, que funciona bien, pero no sería seguro hilo. Si se trata de una atomic<int>
, entonces el compilador me dice esto:
g++ foo.cc
foo.cc:34:38: error: use of deleted function 'Counter::Counter(const Counter&)'
Counter Counter::instance_ = Counter();
^
foo.cc:4:7: note: 'Counter::Counter(const Counter&)' is implicitly deleted because the default definition would be ill-formed:
class Counter
^~~~~~~
foo.cc:4:7: error: use of deleted function 'std::atomic<int>::atomic(const std::atomic<int>&)'
In file included from foo.cc:1:0:
/usr/include/c++/7/atomic:668:7: note: declared here
atomic(const atomic&) = delete;
^~~~~~
No estoy seguro de que me acabo de entender el problema. Cualquier explicación / solución sería muy apreciada.
Salud
Se podría simplificar por tener std::atomic<int> counter_{0};
simplemente un static
miembro de la clase en lugar de parte de cada instancia. (Puesto que usted está asegurando que sólo hay una instancia de la clase.)
O si sólo está utilizando su "Singleton" de esa manera devolver una referencia a un objeto estático, simplemente hacer que todos sus miembros static
por lo que no es necesario ni siquiera obtener un puntero a esa única instancia de ella en el primer lugar. Entonces, sólo puede ser un glorificado namespace{}
con funciones miembro estáticas públicas y privadas. Creo que el único punto de un "Singleton" es retrasar la inicialización hasta después de la inicialización estática mediante el uso de una función estática de ámbito con un inicializador no constante, pero que no está haciendo eso.
El problema real es en el constructor copia de la clase, que su inicializador estático utiliza la forma en que has escrito. Se construye un carácter temporal Counter();
y luego lo copia en estática instance_
variable.
Se puede compilar como C ++ 17, donde elidiendo de dicha copia está garantizada ( obras en Godbolt con g ++ -std=gnu++17
con su fuente intacta ), o se puede reescribir el inicializador
Counter Counter::instance_; // default construct
Counter Counter::instance_{}; // explicitly default construct
Ambos de los trabajos con g ++-std=gnu++11