2016-08-24 4 views
2

J'ai un class que j'utilise à des fins purement syntaxiques, pour appeler une fonction d'une certaine manière. Ceci est un exemple simplifié:Classe non instable en supprimant le destructeur?

#include<iostream> 

template<class T1> 
struct make{ 
    template<class T2> 
    static T1 from(T2 const& t2){ 
     return T1{}; //or something more complicated 
    } 
}; 

int main(){ 
    double d = make<double>::from(2); 
    std::cout << d << '\n'; 
} 

Supposons maintenant que je veux avertir l'utilisateur que cette classe ne doit pas être instanciée. Il peut y avoir des utilisations pour que la classe soit instable mais J'ai la curiosité s'il est possible d'interdire cela?

D'abord, j'essayé de supprimer le constructeur par défaut

template<class T1> 
struct make{ 
    make() = delete; 
    template<class T2> 
    static T1 from(T2 const& t2){ 
     return T1{}; //or something more complicated 
    } 
}; 

Mais cela est encore possible:

make<double> m{}; // valid 

Enfin, j'ai essayé de supprimer le destructor et qui semblait fonctionner

template<class T1> 
struct make{ 
    ~make() = delete; 
    template<class T2> 
    static T1 from(T2 const& t2){ 
     return T1{}; //or something more complicated 
    } 
}; 

Mais il semble que la classe peut encore être allouée par new.

Dois-je supprimer à la fois le destructor et le constructeur de suppression, (quid du constructeur copie et déplacer?)

Est-ce la meilleure façon de désavouer instanciation? Code ici: http://coliru.stacked-crooked.com/a/0299c377c129fffb

#include<iostream> 

template<class T1> 
struct make{ 
    make() = delete; 
    ~make() = delete; 
    template<class T2> 
    static T1 from(T2 const& t2){ 
     return T1{}; //or something more complicated 
    } 
}; 

int main(){ 
    double d = make<double>::from(2); 
    std::cout << d << '\n'; 
    make<double> m{}; // compile error (no destructor) 
    auto p = new make<double>{}; // compile error (no constructor) 
} 

Répondre

3

Mais cela est encore possible:

make<double> m{}; // valid 

... je ne savais pas que cela fonctionnerait. Mais maintenant que je fais, je sais aussi pourquoi cela fonctionne. Et donc, comment l'arrêter.

Cela fonctionne car make<T> comme vous l'avez déclaré est un agrégat. Même s'il possède un constructeur par défaut supprimé, C++ le considère toujours comme un agrégat. Et si vous utilisez braced-init-lists sur un agrégat, vous obtenez aggregate initialization.

La façon de l'arrêter est simple: faire plus un agrégat:

template<class T1> 
struct make{ 
    template<class T2> 
    static T1 from(T2 const& t2){ 
     return T1{}; //or something more complicated 
    } 

    make() = delete; 

private: 
    char c; //Not an aggregate 
}; 

que les forces de membres privées make<T> de ne plus être un agrégat. Par conséquent, il ne peut pas être utilisé avec l'initialisation d'agrégat, donc {} va tenter d'appeler le constructeur par défaut. Ce qui échouera naturellement puisqu'il est supprimé.

La gymnastique pouvant être copiée de façon triviale peut toujours être utilisée pour créer des instances de make<T>. Vous pouvez fermer les bas en lui donnant un virtual destructor:

template<class T1> 
struct make{ 
    template<class T2> 
    static T1 from(T2 const& t2){ 
     return T1{}; //or something more complicated 
    } 

    make() = delete; 

private: 
    char c; //Not an aggregate 
    virtual ~make() = default; 
}; 

Cela devrait être suffisant pour empêcher le code C++ juridique de créer un objet de ce type.

+1

Bonne idée, mais avec votre code c'est toujours autorisé: 'auto p = new make ;', il semble qu'il faille encore supprimer le constructeur (ou le rendre privé). – alfC

+0

@alfC: Je voulais inclure la suppression de la construction.C'est juste une erreur de copier-coller. –

+0

ok, qu'est-ce que vous gagnez en faisant un non-agrégation une fois que le constructeur et les destructeurs sont supprimés ou déclarés privés? – alfC