【问题标题】:Getting around the reinterpret cast limitation with constexpr使用 constexpr 绕过重新解释强制转换限制
【发布时间】:2014-11-29 19:17:06
【问题描述】:

在 c++11 中,constexpr 表达式不能包含重新解释转换。例如,如果想操作浮点数中的位,比如说找到数字的尾数:

constexpr unsigned int mantissa(float x) { 
    return ((*(unsigned int*)&x << 9) >> 9); 
};

上面的代码不能是constexpr。理论上,我看不出在这种或类似情况下的重新解释与算术运算符有何不同,但编译器(和标准)不允许这样做。

有什么巧妙的方法可以绕过这个限制吗?

【问题讨论】:

  • 在非 constexpr 上下文中也不允许这样做。它只是简单的未定义。但是可以使它有效,但这仍然不足以使它成为 constexpr-valid,所以这个问题仍然有效。
  • 为什么要在constexpr 中执行此操作,constexpr 是滥用的新功能吗?
  • @DavidRodríguez-dribeas - 确实如此。我正在尝试探索一些 c++11 功能的边界,这并不是必需的。
  • @hvd - 为什么代码未定义?它可能不是便携式的,但它确实有效:ideone.com/Kh74Ub
  • @nbubis 违反了严格的别名。

标签: c++ c++11 constexpr reinterpret-cast


【解决方案1】:

我看不出在这种或类似情况下的重新解释演员如何 不同于算术运算符

它不是便携式的。

您可能知道您的代码会导致未定义的行为,因为您取消引用类型双关指针并因此破坏了严格的别名。此外,从 C++14 开始,会调用未定义行为的操作甚至不再是常量表达式,因此会产生编译器错误。

您基本上想要做的是将float 对象与一个完整的glvalue 别名。第一步是获得该glvalue;第二个执行左值到右值的转换。

在 C++14 中,第一步不可能在常量表达式中完成。 reinterpret_cast 被明确禁止。和void* 之间的转换,如static_cast&lt;char const*&gt;(static_cast&lt;void const*&gt;(&amp;x)),也不起作用(N3797,[expr.const]/2*):

——从类型 cv void * 到指针对象类型的转换;

请记住,像 (char*) 这样的 c 样式转换被简化为 static_castreinterpret_cast,其限制已在上面列出。 (unsigned*)&amp;x 因此减少到 reinterpret_cast&lt;unsigned*&gt;(&amp;x) 并且不起作用。

在 C++11 中,转换为 void const* 然后转换为 char const* 不构成问题(根据标准;Clang 仍然抱怨后者)。尽管如此,左值到右值的转换是一种:

左值到右值的转换(4.1),除非它应用于
——a 指非易失性的整数或枚举类型的左值 带有前面初始化的 const 对象,用 a 初始化 常量表达式,或
— 一个字面量类型的左值,它引用一个 用constexpr 定义的非易失性对象,或引用一个 此类对象的子对象,或
— 字面量类型的左值 指的是一个非易失性临时对象,其生命周期没有 结束,用常量表达式初始化;

前两个项目符号在这里不适用;也没有任何char/unsigned/等。之前已经初始化了对象,我们也没有用constexpr定义任何这样的对象。

第三个项目符号也不适用。如果我们写

char ch = *(char const*)(void const*)&x;

我们不会在初始化程序中创建char 对象。我们通过char 类型的glvalue 访问x 的存储值,并使用该值初始化ch

因此,我想说这种别名在常量表达式中是不可能的。在某些实施中,您可以通过宽松的规则解决这个问题。


* 该段落是一个以类似开头的列表

一个条件表达式是一个核心常量表达式,除非[...]

(文本从 N3337 到 N3797 不同。)

【讨论】:

    【解决方案2】:

    您获取 float 数字尾数的特定示例实际上对于 numbers 实现非常简单,根本不需要类型双关语,因此以 constexpr 方式实现它。唯一的问题是当你想破解 NaN 时。

    由于您已经依赖 float 是 IEEE 754 中的 binary32,我们可以假设相同,但以另一种方式 - 呈现结果。见以下代码:

    #include <limits>
    constexpr float abs(float x) { return x<0 ? -x : x; }
    
    constexpr int exponent(float x)
    {
        return abs(x)>=2 ? exponent(x/2)+1 :
               abs(x)<1  ? exponent(x*2)-1 : 0;
    }
    
    constexpr float scalbn(float value, int exponent)
    {
        return exponent==0 ? value : exponent>0 ? scalbn(value*2,exponent-1) :
                                                  scalbn(value/2,exponent+1);
    }
    
    constexpr unsigned mantissa(float x)
    {
        return abs(x)<std::numeric_limits<float>::infinity() ?
                    // remove hidden 1 and bias the exponent to get integer
                    scalbn(scalbn(abs(x),-exponent(x))-1,23) : 0;
    }
    
    #include <iostream>
    #include <iomanip>
    #include <cstring>
    
    int main()
    {
        constexpr float x=-235.23526f;
        std::cout << std::hex << std::setfill('0');
        // Show non-constexpr result to compare with
        unsigned val; std::memcpy(&val,&x,sizeof val);
        std::cout << std::setw(8) << (val&0x7fffff) << "\n";
        // Now the sought-for constexpr result
        constexpr auto constexprMantissa=mantissa(x);
        std::cout << std::setw(8) << constexprMantissa << "\n";
    }
    

    its live demo

    【讨论】:

      【解决方案3】:

      从 C++20 开始,有一个标准库解决方案:std::bit_cast(自 GCC 11 起在 GCC 中受支持)。下面是一个使用它的例子:

      #include <bit>
      #include <cstdint>
      #include <iomanip>
      #include <iostream>
      
      int main()
      {
          constexpr float x=-235.23526f;
          constexpr auto integer=std::bit_cast<std::uint32_t>(x);
          constexpr auto mantissa=integer&0x7fffff;
          static_assert(mantissa==0x6b3c3a);
          std::cout << std::hex << std::setfill('0')
                    << std::setw(8) << mantissa << "\n";
      }
      

      its live demo

      【讨论】:

        猜你喜欢
        • 2016-07-27
        • 1970-01-01
        • 1970-01-01
        • 2020-09-06
        • 1970-01-01
        • 1970-01-01
        • 2021-01-05
        • 2014-08-15
        相关资源
        最近更新 更多