在 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_type
和 value_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。
当声明继承自 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_type
和 value_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。