2014-08-28 5 views
5

je le code suivant avec une classe Variant personnalisé et une classe SmartPtr personnalisée:conversion automatique de bool nullptr_t

using namespace std; 

class Object 
{ 
public: 
}; 

template<typename T> 
class SmartPtr 
{ 
public: 

    template<typename Y> 
    explicit SmartPtr(Y* p) { p_ = p; } 

    SmartPtr(std::nullptr_t) { p_ = nullptr; } 

private: 
    T* p_; 
}; 

class Variant 
{ 
public: 
    Variant(bool b) : _b(b) { } 

private: 
    bool _b; 
}; 

class Obj 
{ 
public: 
    void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; } 
    void test(Variant /*v*/) { cout << "variant version!" << endl; } 
}; 

int main(int argc, const char *argv[]) 
{ 
    Obj o; 
    o.test(nullptr); // calls SmartPtr version 
    o.test(true); // calls Variant version 
    o.test(false); // -> compiler error: ambiguous call to overloaded function 

    return 0; 
} 

Je suppose que le faux booléen peut être converti à la fois à la variante et à 0 puis nullptr et puis à SmartPtr, ce qui provoque cette erreur.

Y a-t-il des chances d'éviter cette conversion?

Pour l'utilisateur de la bibliothèque, une API qui fonctionne avec 'o.test (true);' mais nécessite quelque chose comme 'o.test (Variante (faux));' compiler n'est pas très intuitif.

+0

Différents compilateurs traitent ce code différemment. g ++ (C++ 1y) échoue à la fois sur 'nullptr' et 'false'. clang (C++ 1y) échoue à 'nullptr'. Question connexe: http://stackoverflow.com/questions/17501942/false-implicitly-convert-to-null-pointer/ – zch

+0

Je pense que le compilateur compatible standard devrait accepter tout le code ('nullptr_t -> SmartPtr' étant mieux que' nullptr_t -> bool -> Variant' et 'bool -> nullptr_t -> SmartPtr' étant invalide), mais je suppose que cela ne vous aide pas beaucoup. Je recommanderais d'utiliser des noms de fonctions distincts. – zch

+0

@zsh: J'utilise à la fois clang et g ++ avec std = C++ 11 sur linux ainsi que vc12 sur windows, le message d'erreur est le même. Malheureusement, une dénomination différente est hors de question car le code fonctionnait avant et j'essaie d'introduire le constructeur nullptr_t pour la classe SmartPtr qui fait échouer la compilation. – mgr

Répondre

1

Je crois que j'ai une solution idéale. Il exige seulement que la fonction de test soit modifiée, donc il laisse SmartPtr et Variante seul, ce qui est idéal. Il ajoute une surcharge de modèle non définie à tester qui a des spécialisations pour bool et nullptr qui sont définies. Ceci distribue directement bool et nullptr à la spécialisation désirée, mais provoque des erreurs de liaison sur d'autres types non gérés. Je suis tellement content de l'avoir fait, parce que j'ai moi-même couru sous de nombreuses formes. Je souhaite que vous pourriez utiliser explicite des paramètres de fonction !!

J'ai eu l'idée d'ici: C++ templates that accept only certain types

using namespace std; 

class Object 
{ 
public: 
}; 

class Variant 
{ 
public: 
    Variant(bool b) : _b(b) { } 

private: 
    bool _b; 
}; 

template<typename T> 
class SmartPtr 
{ 
public: 
    SmartPtr(std::nullptr_t null) { p_ = nullptr; } 

    template<typename Y> 
    SmartPtr(Y* p) { p_ = p; } 

private: 
    T* p_; 
}; 

class Obj 
{ 
public: 
    void test(SmartPtr<Object> here /*p*/) { 
     cout << "smartptr version!" << endl; 
    } 
    void test(Variant /*v*/) { cout << "variant version!" << endl; } 

    template<typename T> void test(T t); 

    template<> 
    void test<bool>(bool b) { 
     cout << "bool specialization" << endl; 
     test(Variant(b)); 
    } 

    template<> 
    void test<std::nullptr_t>(std::nullptr_t null) { 
     cout << "nullptr specialization" << endl; 
     test(SmartPtr<Object>(nullptr)); 
    } 
}; 

int main(int argc, const char *argv[]) 
{ 
    Obj o; 
    Obj c; 
    Object object; 

    //o.test(3); // Gives link error LNK2019 

    o.test(Variant(true)); // calls Variant version 
    o.test(SmartPtr<Object>(&object)); // calls SmartPtr version 
    o.test(nullptr); // dispatched to SmartPtr version by nullptr specialization 
    o.test(true); // dispatched to Variant version by bool specialization 
    o.test(false); // dispatched to Variant version by bool specialization 
    return 0; 
} 

j'avais déjà répondu quelque chose pas idéal, donc je laisse cette réponse dans le tact que ce qui suit:

======= ==========================================

Je n'ai pas de solution idéale ici, et je ne connais pas les contraintes que vous avez sur votre code, donc cela peut ne pas vous être utile, mais ce qui suit est raisonnable. Il interdit au code d'utiliser nullptr au moment de la compilation et repose sur une constante globale null_smart à utiliser dans tous les cas où l'appelant ne montre aucun intérêt à transmettre un objet.

#include <iostream> 

using namespace std; 

class Object 
{ 
public: 
}; 

class Variant 
{ 
public: 
    Variant(bool b) : _b(b) { } 
private: 
    Variant(std::nullptr_t) {}; 

private: 
    bool _b; 
}; 

template<typename T> 
class SmartPtr 
{ 
public: 
    SmartPtr() { p_ = nullptr; } 

    template<typename Y> 
    SmartPtr(Y* p) { p_ = p; } 

private: 
    T* p_; 
}; 

class Obj 
{ 
public: 
    void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; } 
    void test(Variant /*v*/) { cout << "variant version!" << endl; } 
}; 

const SmartPtr<Object> null_smart; 

int main(int argc, const char *argv[]) 
{ 
    Obj o; 
    o.test(null_smart); // calls SmartPtr version, without interest in passing object 
    o.test(true); // calls Variant version 
    o.test(false); // calls Variant version 
    return 0; 
} 

Il est plus propre que le vrai/Variant (faux) problème, mais encore un peu sur le côté pointilleux.

+0

L'objet avec la méthode test() est en code utilisateur, qui existait avant mon idée d'avoir une syntaxe nulle de pointeur uniforme pour toutes les classes SmartPtr brutes et personnalisées. – mgr