2014-08-28 7 views
5

Ich habe den folgenden Code mit einer benutzerdefinierten Variant-Klasse und eine benutzerdefinierte SmartPtr Klasse:automatische Konvertierung von 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; 
} 

Ich gehe davon aus, dass die boolean false sowohl für die Variante und zu 0 umgewandelt werden kann dann nullptr und dann zu SmartPtr, was diesen Fehler verursacht.

Alle Chancen, diese Konvertierung zu vermeiden?

Für den Benutzer der Bibliothek eine API, die mit 'o.test (true);' aber erfordert etwas wie 'o.test (Variant (false));' zu kompilieren ist nicht sehr intuitiv.

+0

Verschiedene Compiler behandeln diesen Code anders. g ++ (C++ 1y) schlägt bei "nullptr" und "false" fehl. clang (C++ 1y) schlägt bei "nullptr" fehl. Verwandte Frage: http://stackoverflow.com/questions/17501942/false-implicitly-convert-to-null-pointer/ – zch

+0

Ich denke, dass Standard Compiler sollte den gesamten Code akzeptieren ('nullptr_t -> SmartPtr' ist besser als 'nullptr_t -> bool -> Variante' und 'bool -> nullptr_t -> SmartPtr' ist ungültig), aber ich schätze, es hilft dir nicht viel. Ich würde empfehlen, separate Funktionsnamen zu verwenden. – zch

+0

@zsh: Ich benutze sowohl clang und g ++ mit std = C++ 11 auf Linux als auch vc12 auf Windows, die Fehlermeldung ist die gleiche. Unglücklicherweise kommt eine andere Benennung nicht in Frage, da der Code vorher funktioniert hat und ich versuche, den nullptr_t-Konstruktor für die SmartPtr-Klasse einzuführen, der die Kompilierung fehlschlägt. – mgr

Antwort

1

Ich glaube, ich habe eine ideale Lösung. Es erfordert nur, dass die Testfunktion geändert wird, so dass SmartPtr und Variant alleine gelassen werden, was ideal ist. Es wird eine nicht definierte überarbeitete Testversion hinzugefügt, die über Spezialisierungen für bool und nullptr verfügt, die definiert sind. Dadurch werden bool und nullptr direkt an die gewünschte Spezialisierung gesendet, Linkfehler werden jedoch bei anderen nicht behandelten Typen verursacht. Ich bin froh, dass das funktioniert hat, denn ich bin mir selbst in vielen Formen begegnet. Ich wünschte, du könntest explizite Funktionsparameter verwenden !!

habe ich die Idee von hier: 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; 
} 

ich schon mit etwas nicht ideal beantwortet hatte, also lasse ich diese Antwort in Takt wie das, was folgt:

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

Ich habe keine ideale Lösung Hier, und ich kenne nicht die Einschränkungen, die Sie auf Ihrem Code haben, so dass dies für Sie nicht von Nutzen sein kann, aber das Folgende ist sinnvoll. Es verbietet es dem Code, zur Kompilierzeit nullptr zu verwenden und verlässt sich auf eine globale null_smart-Konstante, die in allen Fällen verwendet wird, in denen der Aufrufer einfach kein Interesse daran zeigt, ein Objekt zu übergeben.

#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; 
} 

Es ist sauberer als das wahre/Variant (false) Problem, aber immer noch ein bisschen auf der Seite pingelig.

+0

Das Objekt mit der test() - Methode befindet sich im Benutzercode, der vor meiner Idee existierte, eine einheitliche Null-Pointer-Syntax für alle rohen und benutzerdefinierten SmartPtr-Klassen zu haben. – mgr

Verwandte Themen