在编译时将函数指针转换为 std::uintptr_t

xii*_*408 3 c++ templates constexpr

我想std::uintptr_t在编译时将常量表达式函数指针转换为 a 。我怎样才能做到这一点?

这是一个最小的例子:

#include <cstdint>
void fn() {}
int main(int argc, char** argv) {
  constexpr void* ptr = (void *) fn;
  constexpr std::uintptr_t idx = reinterpret_cast<std::uintptr_t>(fn);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

GCC 7/8/9 目前给出错误“std::uintptr_t在常量表达式中从指针类型转换为算术类型”。然而,我的理解是std::uintptr_t应该能够保存任何指针类型,这意味着这应该能够在常量表达式中完成。

背景

为了说明为什么我需要这个,我想(1)在编译时检索函数指针的地址,(2)将其转换为 a std::uintptr_t,然后(3)将其作为模板参数传递,以便它可以在编译时将其烘焙到函数中。

这是 RPC 引擎的一部分,类似于此代码,它会产生非常相似的错误:

#include <cstdio>
#include <cstdint>

template <std::uintptr_t FnPtr, typename Fn>
void fn_handler() {
  ((Fn *) FnPtr)();
}

int main(int argc, char** argv) {
  auto lel = []() {
    printf("Hi, fam!\n");
  };
  // Note that +lel is an implement 0+lel, converting
  // the lambda to a fn ptr.
  constexpr void* ptr = reinterpret_cast<void*>(+lel);
  constexpr std::uintptr_t idx = reinterpret_cast<std::uintptr_t>(ptr);

  fn_handler<idx, decltype(lel)>();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 5

但是,我的理解是 std::uintptr_t 应该能够保存任何指针类型,这意味着这应该能够在常量表达式中完成。

当然。但在常量表达式中reinterpret_cast绝不允许。由于这是将指针转换为整数的唯一方法,因此您无法在编译时执行此操作。

如果您想将函数指针作为模板参数传递,那么只需这样做

int func() {return 0;}

template<int (*pfn)()>
int fn_handler()
{
  return pfn();
}

...

fn_handler<&func>();
Run Code Online (Sandbox Code Playgroud)

如果你想让函数的类型成为模板参数,那么在 C++17 之前,你可以使用这样的老技巧:

template<typename Fn, Fn pfn>
decltype(auto) fn_handler()
{
  return pfn();
}

...

fn_handler<decltype(&func), &func>();
Run Code Online (Sandbox Code Playgroud)

C++17 让我们只使用auto

template<auto pfn>
decltype(auto) fn_handler()
{
  return pfn();
}

...

fn_handler<&func>();
Run Code Online (Sandbox Code Playgroud)