2016-05-15 26 views
1

Wenn eine Klassenvorlage erklärt, die von std::unordered_map erbt, erhalte ich die falsche Größe der Vorlage Argument, wenn in Visual C läuft ++ 2015Falsche sizeof() der Template-Argument, wenn sie von unordered_map in visual c vererben ++

Die Code unten funktioniert wie auf Ubuntu 64-Bit erwartet, wenn sie mit

g++ -std=c++11 test.cpp 

Ausgeben der folgenden zusammengestellt:

OUTSIDE: sizeof(my_key_type) = 12, sizeof(my_value_type) = 24 
INSIDE: sizeof(my_key_type) = 12, sizeof(my_value_type) = 24 
INSIDE(WTF?): sizeof(key_type) = 12, sizeof(value_type) = 24 

Aber in Visual C++ 2015 auf einer 64-Bit-Maschine, ich bin immer:

OUTSIDE: sizeof(my_key_type) = 12, sizeof(my_value_type) = 24 
INSIDE: sizeof(my_key_type) = 12, sizeof(my_value_type) = 24 
INSIDE(WTF?): sizeof(key_type) = 12, sizeof(value_type) = 40 

Wenn ich von std::unordered_map nicht erbt, dann alles, was sowohl auf Ubuntu und in VC2015 gut funktioniert. Was fehlt mir hier?

Vielen Dank im Voraus für Ihre Hilfe - hier ist der Code:

#include <stdio.h> 
#include <string.h> 
#include <string> 
#include <unordered_map> 

class my_key_type { 
    unsigned int _int1; 
    unsigned int _int2; 
    unsigned short _short1; 
    unsigned short _short2; 
public: 
    bool operator == (const my_key_type &other_key) const { 
     return (memcmp(this, &other_key, sizeof(my_key_type)) == 0); 
    }; 
}; 

namespace std { 
    template <> struct hash<my_key_type> { 
     std::size_t operator()(const my_key_type &key) const { 
      return std::hash<string>()(std::string((const char *)&key, sizeof(my_key_type))); 
     }; 
    }; 
}; 

class my_value_type { 
    bool _flag; 
    unsigned long long _count1; 
    unsigned long long _count2; 
}; 

#define INHERITS_FROM_UNORDERED_MAP 1 
#if (INHERITS_FROM_UNORDERED_MAP == 0) 
template <typename key_type, typename value_type> class kv_map { 
#else 
template <typename key_type, typename value_type> class kv_map : public std::unordered_map<key_type, value_type> { 
#endif 
public: 
    void test_print() { 
     printf("INSIDE: sizeof(my_key_type) = %ld, sizeof(my_value_type) = %ld\n", sizeof(my_key_type), sizeof(my_value_type)); 
     printf("INSIDE(WTF?): sizeof(key_type) = %ld, sizeof(value_type) = %ld\n", sizeof(key_type), sizeof(value_type)); 
    }; 
}; 

int main() { 
    printf("OUTSIDE: sizeof(my_key_type) = %ld, sizeof(my_value_type) = %ld\n", sizeof(my_key_type), sizeof(my_value_type)); 
    kv_map<my_key_type, my_value_type> map; 
    map.test_print(); 
}; 
+0

Ich würde vermuten, dass es etwas mit Padding zu tun hat, die vom Compiler zwischen dem 'bool' und dem folgenden' unsigned long long' hinzugefügt wurde. Außerdem würde ich den V-Tabellenzeiger überprüfen, der zu Ihrer Klasse hinzugefügt wird, wenn er von einer (vermutlich) Klasse erbt, die virtuelle Funktionen deklariert. Verschiedene Compiler verwenden dafür möglicherweise verschiedene Implementierungen (obwohl ich bezweifle, dass dies in diesem Fall der Fall ist). –

+0

['std :: unordered_map'] (http://en.cppreference.com/w/cpp/container/unordered_map) ist nicht darauf ausgelegt, von geerbt zu werden, es hat keinen virtuellen Destruktor. Oder es hat keine * virtuellen * Funktionen, aber es ist das Fehlen eines virtuellen Destruktors, der Sie am meisten im Hinterteil gebissen wird. Wenn Sie einen Teil der Schnittstelle erben möchten, können Sie möglicherweise private Vererbung verwenden. –

+0

Um dem zu folgen, was Joachim sagt: Erben Sie nicht von 'std :: unordered_map' - enthalten Sie es. (Verwenden Sie has-a, nicht is-a). –

Antwort

2

std::unordered_map eine Art value_type

value_type std::pair<const Key, T> 

Es ist mit ziemlicher Sicherheit genannt hat, dass Sie, dass bis sind Kommissionierung - wie das beinhaltet Schlüsseldaten ebenso.

Ändern Sie den Namen Ihres Vorlagensatzes und sehen Sie, was passiert.

+0

Irgendwelche Vermutungen welcher Compiler korrekt ist? –

+1

@MartinBonner Immer wenn VS nicht einverstanden ist, verdächtige immer VS. Sie sind freilich nicht einmal C++ 11-konform. Der VS-Grammatik-Parser ist nicht in der Lage, C++ 11 zu analysieren. – xaxxon

+0

Ich weiß, dass VS häufiger falsch ist als GCC - aber beide haben Fehler. Es ist auch eine eher akademische Übung - wenn der Code mit VS kompiliert werden muss, muss man seine Grenzen umgehen (und es ist auf jeden Fall auch für einen Wartungsprogrammierer verwirrend). –

0

Das Problem, imho, hat Vorrang vor dem Lookup vom Typ key_type und value_type.

Sie haben sich dafür entschieden, zwei Typennamen zu verwenden, die bereits in der Klasse std::unordered_map<K,V> vorhanden sind. Insbesondere haben Sie

using value_type = pair<const K, V>; 

Was ist passiert, dass sizeof(value_type) zum typedef des unordered_map auf MSVC und auf Ihrer benutzerdefinierten Vorlage Argument auf Ubuntu bezieht. Ich weiß nicht, wer Recht hat, werde ich einen Blick auf Standard, aber Sie können, dass durch den Versuch sehen, mit

printf("INSIDE(WTF?): sizeof(key_type) = %ld, sizeof(value_type) = %ld\n", sizeof(typename unordered_map<kt, vt>::key_type), sizeof(typename unordered_map<kt, vt>::value_type)); 

in dem Sie die value_type von dem unordered_map zu verwenden, zwingen Sie 40 Bytes bekommen.

1

Ich glaube, das ist ein Fall, in dem VS-Fehler, eine 2-Phasen-Lookup zu tun, zeigt.

Ein Name aus einer Nicht-Template-Basisklasse verdeckt die Namen der Template-Parameter der abgeleiteten Klasse. Zum Beispiel:

class A 
{ 
public: 
    int T: 
}; 

template<typename T> 
class B : A 
{ 
    // here T will mean A::T and not the template parameter 
}; 

Auf der anderen Seite, wenn die Basisklasse ist eine Vorlage, die lokalen Namen nicht während der Phase 1-Namen-Suche bekannt - so werden die Namen zu binden, was an diesem Punkt sichtbar ist.

template<typename X> 
class A 
{ }; 

template<> 
class A<int> 
{ 
    using T = float; 
}; 

template<typename T> 
class B : A<T> 
{ 
    // here a possible A::T is not visible at phase 1 
    // so T must mean B::T 
}; 

VC++ ist bekannt, den Namen Lookup bis Template Instanziierung nicht durchzuführen. Zu diesem Zeitpunkt kennt er die realen Parameter und den realen Basistyp und behandelt Fall 2 scheinbar so, als wäre es Fall 1.

+0

Sehr cool. Ich dachte, es wäre ein VS-Problem, aber ich hatte keine Ahnung, welcher Teil dieses Problems verursachen würde. – xaxxon