Implizite Konvertierung - Wer hat den "Vorzug"?



  • Da eine implizite Konvertierung von double zu int besteht, wird diese zur Überladungsauflösung herangezogen. Warum bietest du keinen Konstruktor an, der ein T nimmt?

    explicit sollten deine Konstruktoren ohnehin sein, aber das hat nichts mit dem Problem zu tun. Ohne explicit sind nämlich solche Dinge möglich:

    Beispiel x = 4; // Grösse 4
    


  • [quote="Nexus"]Da eine implizite Konvertierung von double zu int besteht, wird diese zur Überladungsauflösung herangezogen. Warum bietest du keinen Konstruktor an, der ein T nimmt?

    Da hab ich ein Überladungsproblem wenn T=int. Wie gesagt versuch ich eine Templatespezialisierung zu umgehen, da ich dann entweder über 1500 Zeilen Code doppelt hätte (ausser den Konstruktoren alles gleich) oder irgendwelche mühsamen include Spielereien machen müsste, die der Übersichtlichkeit nicht gerade förderlich sind.

    Warum wird die implizite Konvertierung von double zu int durch das explicit nicht verhindert und dann die Konvertierung zu element_type herangezogen?



  • ccpete schrieb:

    Warum wird die implizite Konvertierung von double zu int durch das explicit nicht verhindert und dann die Konvertierung zu element_type herangezogen?

    Weil explicit nicht das tut. Das Schlüsselwort sagt nicht, dass die Parameter nicht konvertiert werden dürfen. Es sagt nur, dass ein int nicht implizit in ein Beispiel -Objekt umgewandelt werden kann.

    Etwas umständlicher wäre die Lösung über SFINAE. Hier muss der Typ exakt ein int sein. Um long etc. zu erlauben könnte man statt is_same<Integer, int> is_integral<Integer> nehmen.

    #include <type_traits>
    ...
        // Innerhalb der Klasse: Konstruktor mit Dummy-Parameter
        template <typename Integer>
        Beispiel(int _size, std::enable_if<std::is_same<Integer, int>::value>* = NULL)
    

    Der Code setzt einen C++0x-Compiler voraus. Mit TR1 wären enable_if und is_same im Namensraum std::tr1 und der Include-Pfad könnte anders aussehen. Du kannst auch Boost benutzen.

    Wenn du weder die Möglichkeit hast, zu C++0x, zum Technical Report 1 oder zu Boost zu greifen, kann ich auch schnell zeigen, wie das sonst gehen würde.



  • Nexus schrieb:

    Es sagt nur, dass ein int nicht implizit in ein Beispiel -Objekt umgewandelt werden kann.

    Ok, macht Sinn! Gibt es auch eine Begründung, warum die implizite Konvertierung von double auf int der impliziten Konvertierung von double auf element_type vorgezogen wird?

    Nexus schrieb:

    Wenn du weder die Möglichkeit hast, zu C++0x, zum Technical Report 1 oder zu Boost zu greifen, kann ich auch schnell zeigen, wie das sonst gehen würde.

    Wäre toll, ja!



  • Was spricht gegen die "übliche" Variante

    template<class T>
    class Beispiel
    {
      public:
         Beispiel( int _size );
         Beispiel( int _size, T _value );
    
    };
    
    // oder gleich als einzigen ctor:
    
         Beispiel( int _size, T _value = T() );
    
    // bzw.
    
         Beispiel( int _size, T const& _value = T() );
    

    ?



  • ccpete schrieb:

    Ok, macht Sinn! Gibt es auch eine Begründung, warum die implizite Konvertierung von double auf int der impliziten Konvertierung von double auf element_type vorgezogen wird?

    Ja, bei der Überladungsauflösung haben eingebaute implizite Konvertierungen Priorität über benutzerdefinierte Konvertierungen.

    C++-Standard, §13.3.3.2/2 Ranking implicit conversion sequences schrieb:

    When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)
    — a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence [...]

    Allerdings habe ich das ganze bisher zu wenig semantisch angeschaut. Ich kann mich deshalb hustbaer nur anschliessen, für einen Container ist seine Schnittstelle sicher intuitiver (und einfacher zu implementieren).



  • ccpete schrieb:

    Nexus schrieb:

    Es sagt nur, dass ein int nicht implizit in ein Beispiel -Objekt umgewandelt werden kann.

    Ok, macht Sinn! Gibt es auch eine Begründung, warum die implizite Konvertierung von double auf int der impliziten Konvertierung von double auf element_type vorgezogen wird?

    C++ Standard schrieb:

    13.3 Overload resolution

    (...)

    13.3.3.2 Ranking implicit conversion sequences

    (...)

    2 When comparing the basic forms of implicit conversion sequences (as defined in 13.3.3.1)

    — a standard conversion sequence (13.3.3.1.1) is a better conversion sequence than a user-defined conversion sequence or an ellipsis conversion sequence, and

    — a user-defined conversion sequence (13.3.3.1.2) is a better conversion sequence than an ellipsis conversion sequence (13.3.3.1.3).

    double -> int ist eine "standard conversion"
    double -> DeinTyp ist eine "user-defined conversion"

    EDIT: upps, zu langsam



  • hustbaer schrieb:

    Was spricht gegen die "übliche" Variante?

    Nur dass ich gerne einen Konstruktor in der Form meines ersten Postings hätte, weils angenehm ist 🙂

    (Ausserdem ist der Container in der Form schon wacker im Einsatz. Ich habe mir einfach plötzlich gedacht: Hey, immer dieser scheiss redundante Code jedes Mal wenn ich was ergänzen muss und die Files werden dadurch auch nicht schöner - also vielleicht kann man da was tricksen...)

    Nexus schrieb:

    Ja, bei der Überladungsauflösung haben eingebaute implizite Konvertierungen Priorität über benutzerdefinierte Konvertierungen.

    Merci!

    Nexus schrieb:

    ...und einfacher zu implementieren

    An Herausforderungen wächst man!



  • Hm, wie willst du denn die Schnittstelle genau verwenden? Hast du einen kleinen Beispielcode für die Anwendung deines Containers (und insbesondere des zweiten Konstruktors)?

    Denn vielleicht gibts auch eine schönere Möglichkeit...



  • Nexus schrieb:

    Hm, wie willst du denn die Schnittstelle genau verwenden? Hast du einen kleinen Beispielcode für die Anwendung deines Containers (und insbesondere des zweiten Konstruktors)?

    Der zweite Konstruktor wird ganz einfach dafür verwendet, den Container zu erstellen und gleich mit einem ersten Element zu füllen, egal um was für eine Art Container dass es sich dann im Endeffekt wirklich handelt.

    Also anstatt:

    //...
    Object( int _size, T _element );
    //...
    
    Object neu(1,erstesElement);
    

    einfach:

    Object(erstesElement);
    

    Ganz abgesehen von der Anwendung interessiert es mich aber auch ganz einfach, wie (und ob) sowas implementiert werden kann ohne die Templatespezifikation zu bemühen.



  • Ich würde auf jeden Fall Uneindeutigkeiten (EDIT: ich meine eigentlich Unklarheiten) vermeiden. Die Semantik "wenn T ein int ist dann ist container = 5 ne Initialisierung mit nem Element und sonst halt ne Initialisierung mit ner Grössenangabe" ist Murks ^10. Glaub mir das bitte. Das ist nicht praktisch, das ist nur verwirrend.

    http://en.wikipedia.org/wiki/Principle_of_least_astonishment

    Wenn du unbedingt nen ctor mit nur einem Element, ohne Grössenangabe haben willst, dann dreh die Parameter halt um:

    class Foo
    {
    public:
        explicit Foo(T const& t = T(), size_t size = 1); // einziger ctor!
    };
    

    Oder wickel die Grössenangabe in einen Helper-Typ ein den du explizit konstruierst:

    struct Size
    {
        explicit Size(size_t size_) : size(size_) {}
        size_t size;
    };
    
    template <class T>
    class Foo
    {
    public:
        explicit Foo(T const& t = T());
        explicit Foo(Size size, T const& t = T());
    };
    
    Foo<int> f1 = 42; // 1 element mit wert 42
    Foo<std::string> f2 = "sepp"; // 1 element mit wert "sepp"
    
    Foo<int> f3 = Size(42); // 42 elemente
    Foo<std::string> f4 = Size(42); // 42 elemente
    
    Foo<std::string> f4 = Foo<std::string>(Size(42), "sepp"); // 42 elemente mit Inhalt "sepp"
    


  • hustbaer schrieb:

    Ich würde auf jeden Fall Uneindeutigkeiten vermeiden. Die Semantik "wenn T ein int ist dann ist container = 5 ne Initialisierung mit nem Element und sonst halt ne Initialisierung mit ner Grössenangabe" ist Murks ^10. Glaub mir das bitte. Das ist nicht praktisch, das ist nur verwirrend.

    Genau darum soll container(5) immer einen Container mit Grösse 5 erstellen, also im Fall T=int der Konstruktor mit dem ersten Element nur aufrufbar sein, wenn explizit zu element_type gecastet wird.



  • auch so MURKS^10 oder kannst du dich so damit anfreunden?



  • ccpete schrieb:

    Genau darum soll container(5) immer einen Container mit Grösse 5 erstellen, also im Fall T=int der Konstruktor mit dem ersten Element nur aufrufbar sein, wenn explizit zu element_type gecastet wird.

    Container<double> a(7.); // 1 Element
    Container<double> b(7);  // 7 Elemente - ?!
    
    Container<long> m(53l);  // 1 Element
    Container<long> n(53);   // 53 Elemente - ?!
    
    Container<int> x(53);    // Compilerfehler - warum das jetzt?
    Container<int> y(static_cast<typename Container<int>::element_type>(53)); // WTF?!?
    

    Aha. Praktisch und intuitiv zugleich 😉

    Nein, hustbaer hat wirklich Recht. Mit Templates arbeiten heisst generisch arbeiten. Ein Container soll für alle Typen gleich funktionieren, die Sonderbehandlung von int (nur um eine 1 beim Aufruf zu sparen) macht alles massiv komplizierter. Stell dir vor, du machst ein Funktionstemplate, das auf deinem Container operiert. Sobald der Konstruktor verwendet wird, artet dein Code in einer Fallunterscheidung aus, ob int verwendet wird oder nicht. Und ich dachte, sowas wolltest du ursprünglich vermeiden?

    Sogar bei Standardsachen wie std::vector<bool> gibt es soviele Probleme, nur weil sich die Spezialisierung nicht exakt gleich verhält wie der allgemeine Fall. Und im Endeffekt gewinnt man nichts.

    Mach doch ansonsten sowas:

    template <typename T>
    Container<T> OneElementContainer(const T& t);
    


  • Nun gut, leuchtet ein! Jeder ausser mir, der das Ding allenfalls mal benutzen würde, käme wohl ziemlich ins Schwitzen 😃

    Das Interesse selbst wär aber noch vorhanden: Ich meine, wärs überhaupt machbar?



  • ccpete schrieb:

    auch so MURKS^10 oder kannst du dich so damit anfreunden?

    Nö, weil es immer noch Unklar ist (für den Compiler vielleicht eindeutig, aber für den Programmierer/Leser nicht).



  • ccpete schrieb:

    Das Interesse selbst wär aber noch vorhanden: Ich meine, wärs überhaupt machbar?

    Machbar ist (fast) alles irgendwie 🤡


Anmelden zum Antworten