2016-05-15 4 views
1

std::unordered_mapから継承するクラステンプレートを宣言する場合のVisual Cで動作しているとき、私はテンプレート引数の間違ったサイズを取得してい++ 2015間違ってはsizeof()のVisual Cにunordered_mapから継承++

Ubuntuの64ビットで期待どおり

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

は以下を出力してコンパイル時に以下のコードは、動作します:

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 

しかし、ビジュアルでC++ 2015の64ビットマシン上で、私が取得しています:

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 

を私はstd::unordered_mapから継承しない場合は、すべてがUbuntuの上とVC2015の両方で正常に動作します。私はここで何が欠けていますか? - それは含まれて

#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

私はそれが 'bool'と' unsigned long long'の間でコンパイラによって追加されたパディングと関係していると思います。さらに、仮想関数を宣言する(おそらく)クラスから継承するときに、クラスに追加されるVテーブルポインタをチェックします。異なるコンパイラが、異なる実装を使用している可能性があります(この場合、これが問題であるとは考えていますが)。 –

+0

['std :: unordered_map'](http://en.cppreference.com/w/cpp/container/unordered_map)は継承するようには設計されておらず、仮想デストラクタもありません。むしろ、*仮想関数はありませんが、それはあなたの背後で最もよく噛み砕く仮想デストラクタの欠如です。インタフェースの一部を継承したい場合は、プライベート継承を使用する可能性があります。 –

+0

Joachimの言葉に従う: 'std :: unordered_map'から継承しないでください。 (is-aではなくhas-aを使用します)。 –

答えて

2

std::unordered_mapそれはほぼ確実にあなたがそれを拾っていることだvalue_type

value_type std::pair<const Key, T> 

と呼ばれるタイプがあります - あなたの助けを事前に

おかげでここのコードですキーデータも同様です。

テンプレートタイプの名前を変更して何が起こるかを確認してください。

+0

どのコンパイラが正しいのでしょうか? –

+1

@MartinBonner VSが不一致の場合は常に、VSが疑わしい。彼らはC++ 11準拠さえも認められません。 VS文法パーサーは、構造的にC++ 11の構文解析ができません。 – xaxxon

+0

私はVSがGCCよりも間違っていることはよく知っていますが、どちらもエラーがあります。むしろ学問的な演習でもあります。コードをVSでコンパイルする必要がある場合は、その制限を回避しなければなりません(とにかく、メンテナンスプログラマーにとっても混乱します)。 –

0

問題は、key_typevalue_typeの検索の優先順位です。

std::unordered_map<K,V>クラスに既に存在する2つのタイプ名を使用することを選択しました。具体的に、あなたは何が起こる

using value_type = pair<const K, V>; 

sizeof(value_type)はMSVC上とUbuntu上のカスタムテンプレート引数にunordered_mapのtypedefのを参照していることである持っています。私は標準を見てみなければならなく、あなたが

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)); 

を試みることによって、あなたが40バイトを取得しますunordered_mapからvalue_typeを使用するように強制することを確認することができ、右だか分かりません。

1

これは、VSの2フェーズルックアップが失敗した場合の1つのケースだと思います。

非テンプレートベースクラスの名前は、派生クラスのテンプレートパラメータの名前を非表示にします。たとえば:基底クラスがテンプレートである場合

class A 
{ 
public: 
    int T: 
}; 

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

一方、そのローカル名がフェーズ1名のルックアップ中に知られていない - ので、名前がその時点で表示されているものにバインドする必要があります。

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++は、テンプレートインスタンス化まで名前検索を実行しないことが知られています。このとき、実際のパラメータと実際の基底型を認識し、ケース2をケース1のように扱うように見えます。

+0

非常にクールです。私はそれがVSの問題であると考えましたが、それがどの部分でこの種の問題を引き起こすのか分かりませんでした。 – xaxxon

関連する問題