Bonjour à tous,

Je viens à vous car je me heurte à un problème que je n'arrive pas à comprendre.
Je tente d'implémenter une classe singleton Template réutilisable ; en m'inspirant de ce que je trouve sur le net, j'arrive à une première implémentation (non encore thread-safe, mais c'est pas la question), avec des pointeurs classiques, qui marche :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 
#ifndef DEFINE_SINGLETON
#define DEFINE_SINGLETON
 
#include <iostream>
 
 
// Classe de design pattern Singleton
 
template <typename T>
 
class Singleton {
 
protected :
 
  // Pointeur vers l'instance unique
  static T* singletonPtr;
 
  // Constructeur et destructeur privés pour éviter l'appel depuis l'extérieur.
  Singleton () {}
  ~Singleton() {}
 
public :
 
  // Méthode statique pour accéder à l'instance unique du Singleton ; celle-ci est
  // construite si elle n'était pas encore allouée.
  static T* getInstance() {
    if ( singletonPtr == nullptr ) singletonPtr = new T;
    return singletonPtr;
  }
 
  // Méthode statique pour détruire l'instance unique de T
  static void kill() {
    if ( singletonPtr != nullptr) delete singletonPtr;
    singletonPtr = nullptr;
  }    
 
};
 
// définition du singleton statique, intialisé sur nullptr
template <typename T>
T* Singleton<T>::singletonPtr = nullptr;
 
// Classe test utilisant la classe ci-dessus
 
class Test : public Singleton<Test> {
 
  friend class Singleton<Test>;
 
private :
 
  // Constructeur et destructeur private pour éviter l'instanciation/destruction
  Test() {}
  ~Test(){}
 
  // Constructeur de copie et opérateur d'affectation interdits
  Test(const Test& other) = delete;
  Test operator=(const Test& other) = delete;
 
public :
 
  // une méthode quelconque
  void doSomething() { std::cout << "doSomething" << std::endl; }
 
};
 
 
#endif // ndef DEFINE_SINGLETON
et le main pour tester :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
 
#include <iostream>
#include "SingletonSP.hpp"
 
int main() {
  Test::getInstance()->doSomething();
  Test::kill();
  return 0;
};
Ceci compile et marche parfaitement.

Ensuite, j'essaie une version avec des shared_ptr :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
 
#ifndef DEFINE_SINGLETON
#define DEFINE_SINGLETON
 
#include <iostream>
#include <memory>
 
 
// Classe de design pattern Singleton
 
template <typename T>
class SingletonSP {
 
protected :
  // Pointeur vers l'instance unique
  static std::shared_ptr<T> singletonPtr;
 
  // Constructeur et destructeur privés pour éviter l'appel depuis l'extérieur.
  SingletonSP () {}
  ~SingletonSP() {}
 
public :
 
  // Méthode statique pour accéder à l'instance unique du Singleton ; celle-ci est
  // construite si elle n'était pas encore allouée.
  static std::shared_ptr<T> getInstance() {
    if ( !singletonPtr )  singletonPtr = std::make_shared<T>();
    return singletonPtr;
  }
 
  // kill() n'est a priori plus nécessaire, puisque singletonPtr sera détruit à la fin du scope
  // global, désalouant automatiquement l'objet pointé.
 
};
 
// la définition du singleton statique, non initailisée car encapsulée.
template <typename T>
std::shared_ptr<T> Singleton<T>::singletonPtr;
 
// Classe test utilisant la classe ci-dessus
 
class Test : public SingletonSP<Test> {
 
  friend class Singleton<Test>;
  friend std::shared_ptr<Test> std::make_shared<Test>(); // friend pour avoir accès au constructeur
  friend class std::shared_ptr<Test>; // friend pour avoir accès au destructeur
 
private :
 
  // Constructeur et destructeur private pour éviter l'instanciation/destruction
  Test() {}
  ~Test(){}
 
  // Constructeur de copie et opérateur d'affectation interdits
  Test(const Test& other) = delete;
  Test operator=(const Test& other) = delete;
 
public :
 
  // une méthode quelconque
  void doSomething() { std::cout << "doSomething" << std::endl; }
 
};
 
 
#endif // ndef DEFINE_SINGLETON
et le main à peine modifié :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
 
#include <iostream>
#include "SingletonSP.hpp"
 
int main() {
  Test::getInstance()->doSomething(); // <-ligne 9
  return 0;
};
Et là, la belle erreur de compilation, qui en clair me dit qu'à la ligne 9 je fais appel aux constructeur et destructeur de Test, ce qui est interdit car ils sont private.
L'erreur exacte du compilo est, sans toutes les inclusions de la stl (que j'ai remplacées par (...) pour rendre le truc lisible) :
(...)
SingletonSP.hpp:28:62: required from ‘static std::shared_ptr<_Tp1> SingletonSP<T>::getInstance() [with T = Test]’
main.cpp:6:9: required from here
SingletonSP.hpp:52:3: error: ‘Test::Test()’ is private
Test() {}
(...)
main.cpp:9:2: required from here
SingletonSP.hpp:53:3: error: ‘Test::~Test()’ is private
~Test(){}
(...)

Voilà, j'imagine qu'il s'agit d'une subtilité dans l'utilisation des shared_ptr que je ne vois pas, mais justement je ne la vois pas...
Précision, j'utilise g++ 4.8.1 avec les options -Wall, -Wextra et -std=c++11

Merci d'avance,

whityranger