【问题标题】:replace reinterpret_cast by unions in constexpr - good idea?用 constexpr 中的联合替换 reinterpret_cast - 好主意吗?
【发布时间】:2019-08-27 06:34:45
【问题描述】:

我正在使用此代码将指针转换为size_t,它用于::std::hash 函数中,该函数应在编译时散列给定指针,并且由于reinterpret_casts 不允许在constexpr 中使用,我想出了以下解决方案,它按预期工作,但我想知道这是否可以被认为是一个好主意。 如果你能给我一些关于这段代码的可移植性的指示,那也很棒。

union {
   size_t datr;
   void* pointer;
} dummy{.pointer = (void*) somePointer};
size_t pointerAsAsize_t = dummy.datr; // how portable is this?

有没有更好的解决方案——如上所述,我想创建一个在编译时运行的::std::hash<some_pointer*>

【问题讨论】:

  • 你能在多个编译器和平台上进行测试吗?
  • 鉴于 .pointer 已设置但从未读取过,编译器可能根本不会费心设置它,因为它可以假定您从未从 .datr 读取过,除非您先写信给它。
  • “它按预期工作”我相信不在常量评估中(例如模板参数、constexpr 变量初始化器)。
  • 指针值在编译时通常是未知的。目前尚不清楚您打算如何散列未知值。
  • Accessing an inactive union member 是未定义的行为。

标签: c++ c++11 union constexpr


【解决方案1】:

如果您尝试在常量表达式上下文中执行该操作(即:当编译器在编译时强制调用您的代码,例如导致数组大小或模板参数的表达式),您会发现它不会编译。 Constexpr 执行要求任何会在编译时引起 UB 的东西都是格式错误的。而且由于您的代码会引发 UB(通过访问未激活的联合成员),因此它将无法编译。

All three major compilers will fail on this 在常量表达式上下文中。 MSVC(令人惊讶)给出了最好、最直接的错误信息:

(13):错误 C2131:表达式未计算为常量
(9): 注意:失败是由于访问了一个非活动的联合体成员
(9): 注意:见'foo::Foo::integer'的用法

您的代码可能“按预期工作”只是因为您在编译器不必在编译时执行它时调用它。鉴于 GCC/Clang 的错误,如果您将它用作堆栈上数组的数组大小,它可能会调用 GCC/Clang 的可变长度数组语言扩展。如果您将其关闭,您的代码可能无法编译。我的示例使它成为一个全局数组,它不允许 VLA。


有没有更好的办法

不。如果您提供指针(或包含指针的类型)作为源或目标,即使 C++20's bit_cast 也不会是 constexpr。编译时指针是真实的东西,不仅仅是代表地址的数字;在编译时将它们转换为整数根本不是合理的活动。

编译时指针必须指向编译时存在的东西。 没有办法知道它们将指向运行时的位置。因此,散列指针(甚至是指向静态/全局对象的指针)的编译时值的想法从一开始就注定要失败。

【讨论】:

    【解决方案2】:

    我认为在大多数平台上,这段代码都可以工作,但形式上它不能。 size_t 是一个无符号类型,用于保存结果 sizeof(xxx) 构造。不能保证它足够大来存储指针。您可以添加static_assert(sizeof(size_t) == sizeof(void*),"") 或使用std::intptr_t(如果您的库中有)。

    【讨论】:

    • 从任何union 成员中读取的行为是未定义的,除了最后一个被分配的成员。更改 datr 的 typd 是不够的。
    猜你喜欢
    • 2016-01-24
    • 2014-02-24
    • 2015-11-29
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多