在 Visual C++ 中从 unordered_map 继承时,模板参数的 sizeof() 不正确

Incorrect sizeof() of template argument when inheriting from unordered_map in visual c++

当声明继承自 std::unordered_map 的 class 模板时,在 Visual C++ 2015 中 运行 时,模板参数的大小错误。

下面的代码在 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

但是在 64 位机器上的 Visual C++ 2015 中,我得到:

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

std::unordered_map 有一个名为 value_type

的类型
value_type  std::pair<const Key, T>

几乎可以肯定您正在选择它 - 因为它也包括关键数据。

更改模板类型的名称,看看会发生什么。

恕我直言,问题是 key_typevalue_type.

类型查找的优先级

您选择使用 std::unordered_map<K,V> class 中已经存在的两个类型名称。具体来说你有

using value_type = pair<const K, V>;

发生的事情是 sizeof(value_type) 引用 MSVC 上 unordered_map 的类型定义和 Ubuntu 上的自定义模板参数。我不知道谁是对的,我会看一下标准,但你可以通过尝试

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

其中您强制使用 unordered_map 中的 value_type 您将获得 40 个字节。

我相信这是一个 VS 未能执行 2 阶段查找的案例。

来自非模板基础 class 的名称将隐藏派生的 class 模板参数的名称。例如:

class A
{
public:
   int   T:
};

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

另一方面,如果基础 class 是一个模板,其本地名称在第一阶段名称查找期间是未知的 - 因此名称必须绑定到此时可见的内容。

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。