【问题标题】:Using MSVC's __popcnt in a constexpr function在 constexpr 函数中使用 MSVC 的 __popcnt
【发布时间】:2021-11-01 12:37:44
【问题描述】:

背景:我正在尝试在 MSVC 项目中重新利用为 GCC 编写的一些 C++ 代码。我一直在尝试重构代码以使其与 MSVC 编译器兼容。

简化,原来的功能之一是这样的:

[[nodiscard]] constexpr int count() const noexcept {
    return __builtin_popcountll(mask);//gcc-specific function
}

其中 mask 是 64 位成员变量。向 MSVC 的明显转换是:

[[nodiscard]] constexpr int count() const noexcept {
    return __popcnt64(mask); // MSVC replacement
}

但是,它无法编译,因为 __popcnt64 不允许在 constexpr 函数中使用。

我正在使用 C++17,如果可能,我希望避免切换到 C++20。
有没有办法让它工作?

【问题讨论】:

    标签: c++ visual-c++ c++17 bit-manipulation constexpr


    【解决方案1】:

    问题已经回答了。所以,只是一些旁注。

    构建自己的高效弹出函数可能是最好的解决方案。

    为此,您可以重用 Henry S. Warren 所著的“Hacker's Delight”一书中的古老智慧。这本书是从程序员和算法开发人员致力于解决方案的时候开始的,以尽量减少宝贵的汇编语句的使用。 ROM 消耗(是的,确实如此)和性能。

    您会发现有许多非常有效的解决方案,甚至是完全无循环的,而且汇编指令的数量少得惊人。例如,使用分治法。

    值得一游。 . .

    【讨论】:

      【解决方案2】:

      这取决于目标:

      1. 只需在编译时计数位数,也可能在运行时计数。然后只需实现自己的 constexpr 位计数,不要使用__popcnt64。您可以查看 Wikipedia 的 Hamming weight 文章以获取想法。

      2. 在运行时使用popcnt 指令。那么就需要实现编译时/运行时的区分,使用不同的编译时和运行时实现。

      对于 C++20 中的编译时/运行时区别,您应该使用 if (std::is_constant_evaluated()) { ... } else { ... }

      在 MSVC 中,std::is_constant_evaluated 是通过编译器魔术 __builtin_is_constant_evaluated() 实现的,它恰好在 C++17 中编译并正常工作。所以你可以:

      constexpr int popcount(unsigned long long x)
      {
          if (__builtin_is_constant_evaluated())
          {
              return -1; // TODO: count bits
          }
          else
          {
              return __popcnt64(x);
          }
      }
      

      注意: __builtin_popcountll 编译为 popcnt 指令或通过位 hack 进行位计数,具体取决于编译标志。 MSVC __popcnt64 总是编译成 popcnt 指令。如果目标是支持没有 popcnt 指令的旧 CPU,则必须提供 CPU 检测(编译时或运行时,再次取决于目标)和回退,或者不使用 __popcnt64完全没有。

      【讨论】:

        【解决方案3】:

        你不能使一个非constexpr 函数变成一个constexpr 函数。如果他们的标准库没有声明它constexpr,那么就是这样。您必须自己编写,这在 C++17 中会很困难。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-12-16
          • 2020-02-10
          • 2017-08-11
          • 1970-01-01
          • 2023-03-23
          • 2013-12-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多