2014-09-12 7 views
13

Ich schrieb ein kleines Programm mit std :: map hier wie folgt.Wie zu stoppen automatische Umwandlung von Int in Float und umgekehrt in Std :: Karte

int main() 
{ 
    map<int,float>m1; 
    m1.insert(pair<int,float>(10,15.0)); //step-1 
    m1.insert(pair<float,int>(12.0,13)); //step-2 
    cout<<"map size="<<m1.size()<<endl; //step -3 

habe ich eine Karte mit int-Typ als Schlüssel und Schwimmertyp als Wert (Schlüssel-Wert) -Paar für die Karte m1

Schritt-1 Erstellt zur Karte eines normales int-Schwimmerpaares und eingefügt.

Schritt-2 Erstellt ein Cross Float-Int-Paar und eingefügt, um zu mappen. Jetzt weiß ich, dass die implizite Umwandlung dazu führt, dass dieses Paar in die Karte eingefügt wird.

Hier möchte ich nur nicht die implizite Konvertierung stattfinden und Compiler-Fehler sollte angegeben werden.

Welche Art von Änderungen muss ich in diesem Programm/Map machen, damit der comipiler-Flag einen Fehler verursacht, während wir versuchen, Schritt-2-Operation zu machen?

Vielen Dank im Voraus.

Antwort

6

Hier ist ein Vorschlag:

template <typename K, typename V, typename W> 
void map_insert(map<K,V>& m, K k, W w) { 
    V v = w; 
    m.insert(pair<K,V>(k,v)); 
} 

int main() { 
    map<int,float>m1; 
    map_insert(m1, 10, 15.0); 
    map_insert(m1, 12.0, 13); // compiler complains here 
    cout<<"map size="<<m1.size()<<endl; 

Die dritte Template-Parameter ein wenig umständlich ist aber notwendig, von double zu float Gießen zu ermöglichen.

+0

Können Sie näher erläutern, warum der dritte Vorlagenparameter erforderlich ist? – Gareth

+0

Ohne es beschwert sich der Compiler, dass es Vorlagenparameter für den ersten Aufruf von map_insert() nicht übereinstimmen kann, da 15.0 ein Double, kein Float ist. – dshin

+0

Dies ist kaum eine Lösung (oder Antwort) auf sein Problem: es ist nur eine völlig andere API, die vermeidet, ein std :: pair alle zusammen zu konstruieren. Obwohl es in diesem Sinne "funktionieren" würde, fällt es auch in die Kategorie Trivial. –

2

Es kann auch ein einfacher Weg sein, aber das ist, was mir einfiel:

#include <iostream> 
#include <map> 

template<typename Key, typename Value> 
struct typesafe_pair 
{ 
    const Key& key; 
    const Value& value; 
    explicit typesafe_pair(const Key& key, const Value& value): key(key), value(value) {} 

    operator typename std::map<Key, Value>::value_type() { return typename std::map<Key, Value>::value_type(key, value); } 
}; 

int main() 
{ 
    std::map<int,float>m1; 

    m1.insert(std::pair<int,float>(10,15.0)); // allowed 
    m1.insert(std::pair<float,int>(12.0,13)); // allowed!! 

    m1.insert(typesafe_pair<int,float>(10, 15.0)); // allowed 
    m1.insert(typesafe_pair<float, int>(12.0, 13)); // compiler error 

    std::cout << "map size=" << m1.size() << std::endl; //step -3 
} 

EDIT: 1 Jemand der Lage sein, kann eine bessere (sehr effizient) Lösung beteiligt rvalue Referenzen und perfekte Forwarding Magie, die ich noch nicht ganz verstehe.

EDIT 2: Ich denke Carlo Holz die beste Lösung IMHO hat.

3

Dies ist nicht möglich (und auch wenn es möglich ist, dann wäre es Major Hack, die Sie nicht verwenden sollten).

insert nimmt eine als Argument, das ist eine pair<int const,float>. Also, wenn Sie versuchen, eine pair<float, int> einzufügen, sucht der Compiler nach einer Konvertierung, das heißt: ein Konstruktor von pair<int const, float>, die ein pair<float, int> als Argument verwendet, die einfach vorhanden ist. In der Tat habe ich versucht, eine partielle Spezialisierung für dieses Template-Element (die die Konvertierung ermöglicht) zu finden, die dann auf dem verbleibenden Template-Parameter fehlschlagen könnte, aber ich habe es versäumt; es scheint nicht möglich. Wie auch immer, es wäre ein sehr schmutziger Hack, den Sie einfach nicht machen sollten, nur um einen Tippfehler zu vermeiden. An anderer Stelle benötigen Sie diese Konvertierung möglicherweise, und es ist ein Nein, nichts in Namespace-Std sowieso zu definieren.

Was ist die Lösung für "Wie kann ich diese Art von Tippfehlern vermeiden?" ? Hier

ist, was ich in der Regel tun:

1) Alle meine Karten haben ein typedef für ihre Art.
2) Ich verwende dann ::value_type (und ::iterator etc) ausschließlich auf diesen Typ.

Dies ist nicht nur robuster, es ist auch flexibler: Sie können den Containertyp später ändern und der Code wird wahrscheinlich noch funktionieren.

So würde Ihr Code werden:

int main() 
{ 
    typedef std::map<int,float> m_type; 
    m_type m1; 

    m1.insert(m_type::value_type(10,15.0)); // allowed 
    m1.insert(m_type::value_type(12.0,13)); // no risk for a typo. 

Eine alternative Lösung wäre Ihre float in einer benutzerdefinierten Klasse zu wickeln. Dies ist aus (wiederum) Gründen der Flexibilität ohnehin nicht schlecht. Es ist selten nett, Code mit einer std::map<int, builtin-type> geschrieben zu haben, um dann zu realisieren, dass Sie mehr Daten speichern müssen, und glauben Sie mir, dass das viel passiert. Sie können von Anfang an mit einer Klasse beginnen.