C++ 是否可以在可移植C++;03码?

C++ 是否可以在可移植C++;03码?,c++,pointers,hash,language-lawyer,C++,Pointers,Hash,Language Lawyer,是否可以在C++03中对未定义std::hash的指针进行可移植哈希 在C++中,包含指针的哈希表似乎很奇怪,但我想不出有什么办法来制作它们。 我能想到的最接近的方法是执行reinterpret\u cast(ptr),但是uintptr\u t不需要在C++03中定义,而且我不确定即使定义了该值,该值是否可以合法操作。。。这可能吗?你的问题的答案实际上取决于你想要它的“便携性”。许多体系结构都会有一个uintptr\t,但是如果您想要一些可以在DSP、Linux、Windows、AIX、旧的C

是否可以在C++03中对未定义
std::hash
的指针进行可移植哈希

在C++中,包含指针的哈希表似乎很奇怪,但我想不出有什么办法来制作它们。


我能想到的最接近的方法是执行
reinterpret\u cast(ptr)
,但是
uintptr\u t
不需要在C++03中定义,而且我不确定即使定义了该值,该值是否可以合法操作。。。这可能吗?

你的问题的答案实际上取决于你想要它的“便携性”。许多体系结构都会有一个uintptr\t,但是如果您想要一些可以在DSP、Linux、Windows、AIX、旧的Cray机器、ibm390系列机器等上编译的东西,那么您可能必须有一个配置选项,在该选项中定义您自己的“uintptr\t”,如果它不存在于该体系结构中的话

将指针强制转换为整数类型应该可以。如果你把它扔回去,你可能会有麻烦。当然,如果您有许多指针,并且在64位机器上使用32位整数分配了相当大的内存段,则有可能会发生大量冲突。请注意,64位windows的“long”值仍为32位

一般来说,没有。事实上,在C++11中,如果没有
std::hash
,一般情况下这是不可能的

原因在于值和值表示之间的差异

您可能还记得一个非常常见的示例,该示例用于演示值与其表示形式之间的差异:空指针值。许多人错误地认为这个值的表示都是零位。这在任何情况下都不能保证。只有价值才能保证你的行为

另一个例子是,考虑:

int i;
int* x = &i;
int* y = &i;

x == y;  // this is true; the two pointer values are equal
然而,在这下面,
x
y
的值表示可能不同

让我们玩编译器。我们将实现指针的值表示。假设我们需要(出于假设的架构原因)指针至少为两个字节,但值只使用一个字节

我会跳到前面说可能是这样的:

struct __pointer_impl
{
    std::uint8_t byte1; // contains the address we're holding
    std::uint8_t byte2; // needed for architecture reasons, unused
    // (assume no padding; we are the compiler, after all)
};
好的,这是我们的值表示,现在让我们实现值语义。第一,平等:

bool operator==(const __pointer_impl& first, const __pointer_impl& second)
{
    return first.byte1 == second.byte1;
}
因为指针的值实际上只包含在第一个字节中(即使它的表示有两个字节),这就是我们要比较的全部内容。第二个字节是不相关的,即使它们不同

当然,我们需要运营商实施的地址:

__pointer_impl address_of(int& i)
{
    __pointer_impl result;

    result.byte1 = /* hypothetical architecture magic */;

    return result;
}
这个特定的实现重载为给定的
int
获取指针值表示。请注意,第二个字节未初始化!没关系:这对价值来说并不重要

这是我们真正需要的全部,以使这一点得到澄清。假设其余的实现都完成了。:)

现在再次考虑我们的第一个例子,“编译器化”:

对于我们在假设体系结构上的小示例,这充分提供了标准所要求的指针值的保证。但请注意,您永远不能保证
x==y
意味着
memcmp(&x,&y,sizeof(\uu pointer\u impl))==0
。在值表示上没有这样做的要求

现在考虑你的问题:我们如何散列指针?也就是说,我们要实施:

template <typename T>
struct myhash;

template <typename T>
struct myhash<T*> :
    std::unary_function<T*, std::size_t>
{
    std::size_t operator()(T* const ptr) const
    {
        return /* ??? */;
    }
};
也许令人惊讶的是,这是不正确的。要了解原因,请再次设想我们正在实施它:

// okay because we assumed no padding:
typedef std::uint16_t __uintptr_t; // will be used for std::uintptr_t implementation

__uintptr_t __to_integer(const __pointer_impl& ptr)
{
    __uintptr_t result;
    std::memcpy(&result, &ptr, sizeof(__uintptr_t));

    return result;
}

__pointer_impl __from_integer(const __uintptr_t& ptrint)
{
    __pointer_impl result;
    std::memcpy(&result, &ptrint, sizeof(__pointer_impl));

    return result;
}
因此,当我们
重新解释\u cast
指向整数的指针时,我们将使用
\u to \u integer
,返回时,我们将使用
\u from \u integer
。请注意,结果整数的值取决于指针值表示中的位。也就是说,两个相等的指针值可能以不同的整数表示形式结束…这是允许的

这是允许的,因为重新解释的结果是完全由实现定义的;您只能保证相反的
reinterpret\u cast
返回相同的结果

所以这里有第一个问题:在这个实现中,对于相同的指针值,我们的散列结果可能不同

这个想法已经过时了。也许我们可以深入到表示本身并将字节散列在一起。但这显然是同一个问题的结局,这就是对你问题的评论所暗示的。那些讨厌的未使用的表示位总是在路上,并且没有办法知道它们在哪里,所以我们可以忽略它们

我们被卡住了!这是不可能的。总的来说

请记住,在实践中,我们为某些实现进行编译,因为这些操作的结果是实现定义的,所以如果您只注意正确使用它们,它们是可靠的。这就是问题所在:找出实现的保证,你就会没事了

事实上,您使用的大多数消费平台都能很好地处理
std::uintpttr\t
尝试。如果在您的系统上不可用,或者如果您想要一种替代方法,只需将指针中各个字节的散列组合起来即可。所有这些都需要工作,即未使用的表示位始终具有相同的值。事实上,这就是MSVC2012使用的方法

如果我们假设的指针实现总是将
字节2
初始化为常量,那么它也可以在那里工作。但实现并没有任何这样做的要求


希望这能澄清一些事情。

我想你在寻找的不仅仅是sizeof(ptr)并将其视为字符序列?@GuySirton:你是指
无符号字符序列
?我不确定,将指针作为整数读取是否合法?@Mehrdad:任何对象都可以作为
无符号字符序列读取。但是,某些位可能没有定义的值。例如,如果从
struct读取字节
return myhash<std::uintptr_t>()(reinterpret_cast<std::uintptr_t>(ptr));
// okay because we assumed no padding:
typedef std::uint16_t __uintptr_t; // will be used for std::uintptr_t implementation

__uintptr_t __to_integer(const __pointer_impl& ptr)
{
    __uintptr_t result;
    std::memcpy(&result, &ptr, sizeof(__uintptr_t));

    return result;
}

__pointer_impl __from_integer(const __uintptr_t& ptrint)
{
    __pointer_impl result;
    std::memcpy(&result, &ptrint, sizeof(__pointer_impl));

    return result;
}