fonction amie au template variadique

a marqué ce sujet comme résolu.

Salut,

J’allais faire un pattern Singleton de façon à n’instancier ma classe que lorsqu’elle est demandée. Ainsi j’ai pensé que le shared_ptr était tout à fait approprié. Seulement, pour rendre ma classe non-constructible j’ai passé mon constructeur en private, et donc la fonction std::make_shared ne peut pas s’effectuer. La solution ma paraissait alors toute simple, il suffit de rendre la fonction make_shared amie, seulement le template variadique me pose problème, le compilo m’engeule, je n’arrive pas à comprendre la syntaxe.

Est-ce que quelqu’un pourrait me guider vers la bonne syntaxe de ce que je cherche à faire ?

 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
#include <memory>
#include <iostream>

class SharedSingleton
{
    SharedSingleton(const SharedSingleton &) = delete;
    SharedSingleton(SharedSingleton &&) = delete;
    SharedSingleton & operator=(const SharedSingleton &) = delete;
    SharedSingleton & operator=(SharedSingleton &&) = delete;

public:
    static std::shared_ptr<SharedSingleton> get();

    ~SharedSingleton() {std::cout << "destruction de SharedSingleton" << std::endl;}

private:
    SharedSingleton() {std::cout << "construction de SharedSingleton" << std::endl;}

    static std::weak_ptr<SharedSingleton> m_instance;

    template<class ...Args>
    friend std::shared_ptr<SharedSingleton> std::make_shared<SharedSingleton, Args...> (Args&&... args);
};

std::weak_ptr<SharedSingleton> SharedSingleton::m_instance = std::weak_ptr<SharedSingleton>();

std::shared_ptr<SharedSingleton> SharedSingleton::get()
{
    if(m_instance.expired())
    {
        auto instance = std::make_shared<SharedSingleton>();
        m_instance = instance;
        return instance;
    }
    else
        return m_instance.lock();
}

int main()
{
    std::shared_ptr<SharedSingleton> test = SharedSingleton::get();
    std::shared_ptr<SharedSingleton> autreTest = SharedSingleton::get();
    return 0;
}

Sinon, je peux passer par le constructeur de shared_ptr std::shared_ptr<SharedSingleton> instance (new SharedSingleton());
ça me fait faire un appel à new mais j’ai accès au constructeur puisque je suis dans une fonction membre de ma classe. Est-ce mieux que d’ajouter une fonction amie ?

J’allais oublier l’erreur compilateur

1
2
3
main.cpp:22:103: error: invalid use of template-id 'make_shared<SharedSingleton, Args ...>' in declaration of primary template

     friend std::shared_ptr<SharedSingleton> std::make_shared<SharedSingleton, Args...> (Args&&... args);
+0 -0

pas mal, j’y avais pas pensé, c’est tout con

Cependant, l’instance va resté la même tout au long du programme par la suite, je pensais plutôt que lorsque le singleton n’avait plus d’utilisateur, l’instance est détruite

genre :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int main()
{
    {
        std::shared_ptr<SharedSingleton> test = SharedSingleton::get(); // instanciation
        // utilisation
        do_something(); // appel à une fonction qui utilise le Singleton et agira donc sur la même instance
    } // destruction

    {
        std::shared_ptr<SharedSingleton> test = SharedSingleton::get(); // nouvelle instanciation
        // utilisation de la nouvelle instance
    } // destruction de la nouvelle instance

    return 0;
}

bon, ça dérive un peu du Singleton, et je sais pas encore trop si c’est utile pour mon application, je vais voir

+0 -0

Pour commencer, tu as mal fait ton friend :

1
2
template<class ...Args>
friend std::shared_ptr<SharedSingleton> std::make_shared(Args&&... args);

Ensuite si make_shared est amie de ta classe, alors n’importe qui peut appeler make_shared, cela rend donc tout à fait inutile de mettre ton constructeur en privé.

Ce que tu cherches à faire est infaisable via friend (ce n’est pas make_shared qui appelle directement le constructeur). Donc oui,new est une solution.

Une autre solution, un peu plus complexe, consiste à mettre ton constructeur public avec un pseudo-argument dont le type est privé.

 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
#include <memory>
#include <iostream>

class SharedSingleton
{
    SharedSingleton(const SharedSingleton &) = delete;
    SharedSingleton(SharedSingleton &&) = delete;
    SharedSingleton & operator=(const SharedSingleton &) = delete;
    SharedSingleton & operator=(SharedSingleton &&) = delete;
    
    struct PrivateCtr{};

public:
    static std::shared_ptr<SharedSingleton> get();

    explicit SharedSingleton(PrivateCtr) {std::cout << "construction de SharedSingleton" << std::endl;}
    ~SharedSingleton() {std::cout << "destruction de SharedSingleton" << std::endl;}

private:
    static std::weak_ptr<SharedSingleton> m_instance;
};

std::weak_ptr<SharedSingleton> SharedSingleton::m_instance = std::weak_ptr<SharedSingleton>();

std::shared_ptr<SharedSingleton> SharedSingleton::get()
{
    if(m_instance.expired())
    {
        auto instance = std::make_shared<SharedSingleton>(PrivateCtr{});
        m_instance = instance;
        return instance;
    }
    else
        return m_instance.lock();
}

int main()
{
    std::shared_ptr<SharedSingleton> test = SharedSingleton::get();
    std::shared_ptr<SharedSingleton> autreTest = SharedSingleton::get();
    return 0;
}

Ce code n’est pas thread-safe par ailleurs.

Le pattern simple proposé par Ksass`Peuk est à prendre en considération si ça suffit à tes besoins (il se peut que tu veuilles contrôler le moment de la destruction de ton singleton par exemple, auquel cas ce n’est pas suffisant).

+0 -0

Pour commencer, tu as mal fait ton friend :

1
2
template<class ...Args>
friend std::shared_ptr<SharedSingleton> std::make_shared(Args&&... args);
germinolegrand

Ouais c’est justement ce que je demande, mais cette syntaxe ci ne fonctionne pas non plus, j’avais déjà essayé

Ensuite si make_shared est amie de ta classe, alors n’importe qui peut appeler make_shared, cela rend donc tout à fait inutile de mettre ton constructeur en privé.

germinolegrand

. . . oui. facepalm
j’suis fatigué x)

Ce que tu cherches à faire est infaisable via friend (ce n’est pas make_shared qui appelle directement le constructeur). Donc oui,new est une solution.

Une autre solution, un peu plus complexe, consiste à mettre ton constructeur public avec un pseudo-argument dont le type est privé.

 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
#include <memory>
#include <iostream>

class SharedSingleton
{
    SharedSingleton(const SharedSingleton &) = delete;
    SharedSingleton(SharedSingleton &&) = delete;
    SharedSingleton & operator=(const SharedSingleton &) = delete;
    SharedSingleton & operator=(SharedSingleton &&) = delete;
    
    struct PrivateCtr{};

public:
    static std::shared_ptr<SharedSingleton> get();

    explicit SharedSingleton(PrivateCtr) {std::cout << "construction de SharedSingleton" << std::endl;}
    ~SharedSingleton() {std::cout << "destruction de SharedSingleton" << std::endl;}

private:
    static std::weak_ptr<SharedSingleton> m_instance;
};

std::weak_ptr<SharedSingleton> SharedSingleton::m_instance = std::weak_ptr<SharedSingleton>();

std::shared_ptr<SharedSingleton> SharedSingleton::get()
{
    if(m_instance.expired())
    {
        auto instance = std::make_shared<SharedSingleton>(PrivateCtr{});
        m_instance = instance;
        return instance;
    }
    else
        return m_instance.lock();
}

int main()
{
    std::shared_ptr<SharedSingleton> test = SharedSingleton::get();
    std::shared_ptr<SharedSingleton> autreTest = SharedSingleton::get();
    return 0;
}
germinolegrand

Merci, je vais regarder ça d’un peu plus près

Ce code n’est pas thread-safe par ailleurs.

germinolegrand

Je ne me suis pas encore préoccupé de cet aspect

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte