用 constexpr 中的联合替换 reinterpret_cast - 好主意?
replace reinterpret_cast by unions in constexpr - good idea?
我正在使用此代码将指针转换为 size_t
,它用在 ::std::hash
函数中,该函数应在编译时散列给定的指针,并且由于 reinterpret_cast
constexpr
中不允许使用 s,我想出了以下解决方案,它按预期工作,但我想知道这是否可以被视为一个好主意。
如果您能就这段代码的实际可移植性给我一些建议,那也很棒。
union {
size_t datr;
void* pointer;
} dummy{.pointer = (void*) somePointer};
size_t pointerAsAsize_t = dummy.datr; // how portable is this?
有没有更好的解决方案-如上所述,我想创建一个在编译时运行的::std::hash<some_pointer*>
。
我认为这段代码可以在大多数平台上运行,但形式上它不能。
size_t
是一个无符号类型,用于保存结果 sizeof(xxx)
构造。不能保证它足够大以存储指针。您可以添加 static_assert(sizeof(size_t) == sizeof(void*),"")
或使用 std::intptr_t
(如果您的图书馆中有)。
如果您尝试在常量表达式上下文中执行此操作(即:当编译器在编译时被迫调用您的代码时,例如导致数组大小或模板参数的表达式),您会发现它不会编译。 Constexpr 执行要求任何在编译时会引发 UB 的东西都将变成病式的。并且由于您的代码引发了 UB(通过访问一个不活跃的联合成员),它将无法编译。
All three major compilers will fail on this 在常量表达式上下文中。 MSVC(令人惊讶地)给出了最好、最直接的错误消息:
(13): error C2131: expression did not evaluate to a constant
(9): note: failure was caused by accessing a non-active member of a union
(9): note: see usage of 'foo::Foo::integer'
您的代码可能 "works as expected" 只是因为您在编译器不必在编译时执行它时调用它。考虑到 GCC/Clang 的错误,如果您将它用作堆栈上数组的数组大小,它可能会调用 GCC/Clang 的可变长度数组语言扩展。如果您关闭它,您的代码可能无法编译。我的示例使它成为一个全局数组,它不允许 VLA。
Is there any better solution
没有。如果您提供指针(或包含指针的类型)作为源或目标,即使 C++20's bit_cast
也不会是 constexpr
。编译时指针是真实的东西,而不仅仅是代表地址的数字;在编译时将它们 to/from 转换为整数根本不合理 activity。
编译时指针必须指向编译时存在的东西。 无法知道它们将指向运行时的位置。因此,散列指针(甚至是指向 static/global 对象的指针)的编译时值的想法从一开始就注定要失败。
我正在使用此代码将指针转换为 size_t
,它用在 ::std::hash
函数中,该函数应在编译时散列给定的指针,并且由于 reinterpret_cast
constexpr
中不允许使用 s,我想出了以下解决方案,它按预期工作,但我想知道这是否可以被视为一个好主意。
如果您能就这段代码的实际可移植性给我一些建议,那也很棒。
union {
size_t datr;
void* pointer;
} dummy{.pointer = (void*) somePointer};
size_t pointerAsAsize_t = dummy.datr; // how portable is this?
有没有更好的解决方案-如上所述,我想创建一个在编译时运行的::std::hash<some_pointer*>
。
我认为这段代码可以在大多数平台上运行,但形式上它不能。
size_t
是一个无符号类型,用于保存结果 sizeof(xxx)
构造。不能保证它足够大以存储指针。您可以添加 static_assert(sizeof(size_t) == sizeof(void*),"")
或使用 std::intptr_t
(如果您的图书馆中有)。
如果您尝试在常量表达式上下文中执行此操作(即:当编译器在编译时被迫调用您的代码时,例如导致数组大小或模板参数的表达式),您会发现它不会编译。 Constexpr 执行要求任何在编译时会引发 UB 的东西都将变成病式的。并且由于您的代码引发了 UB(通过访问一个不活跃的联合成员),它将无法编译。
All three major compilers will fail on this 在常量表达式上下文中。 MSVC(令人惊讶地)给出了最好、最直接的错误消息:
(13): error C2131: expression did not evaluate to a constant
(9): note: failure was caused by accessing a non-active member of a union
(9): note: see usage of 'foo::Foo::integer'
您的代码可能 "works as expected" 只是因为您在编译器不必在编译时执行它时调用它。考虑到 GCC/Clang 的错误,如果您将它用作堆栈上数组的数组大小,它可能会调用 GCC/Clang 的可变长度数组语言扩展。如果您关闭它,您的代码可能无法编译。我的示例使它成为一个全局数组,它不允许 VLA。
Is there any better solution
没有。如果您提供指针(或包含指针的类型)作为源或目标,即使 C++20's bit_cast
也不会是 constexpr
。编译时指针是真实的东西,而不仅仅是代表地址的数字;在编译时将它们 to/from 转换为整数根本不合理 activity。
编译时指针必须指向编译时存在的东西。 无法知道它们将指向运行时的位置。因此,散列指针(甚至是指向 static/global 对象的指针)的编译时值的想法从一开始就注定要失败。