c++ - constexpr 函数中的静态表

标签 c++ c++17 constexpr

我有一段将整数映射到整数的生成代码,其核心是一个简单的表。在 C++17 之前,它看起来像这样:

int convert (int v)
{
  static const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };

  if (0 <= v && v < sizeof table / sizeof table[0])
    return table[v];
  else
    return -1;
}

对于 C++17,我想使用 constexpr。我希望将 constexpr 添加到函数签名就足够了,但我必须删除表的 static,这使我的实现更加复杂显然没有充分的理由。更不用说非 constexpr 上下文中的 table 可能会在堆栈上,所以我想我应该 replace staticconstexpr.

G++ 8 报告:

/tmp/foo.cc: In function 'constexpr int convert(int)':
/tmp/foo.cc:14:26: error: 'table' declared 'static' in 'constexpr' function
   static const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };
                          ^

和 Clang++ 7:

/tmp/foo.cc:14:20: error: static variable not permitted in a constexpr function
  static const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };
                   ^
1 error generated.

因为我希望这段代码适用于所有 C++ 标准(并且在每种情况下都做正确的事情),所以我想我必须这样写(是的,宏,这不是问题所在):

#if 201703L <= __cplusplus
# define CONSTEXPR constexpr
# define STATIC_ASSERT static_assert
# define STATIC_OR_CONSTEXPR constexpr
#else
# include <cassert>
# define CONSTEXPR
# define STATIC_ASSERT assert
# define STATIC_OR_CONSTEXPR static
#endif

CONSTEXPR int convert (int v)
{
  STATIC_OR_CONSTEXPR const int table[] = { 3, 2, 6, 1, 7, 1, 6, 8 };

  if (0 <= v && v < sizeof table / sizeof table[0])
    return table[v];
  else
    return -1;
}

int main()
{
  STATIC_ASSERT(convert(-42) == -1);
  STATIC_ASSERT(convert(2) == 6);
  STATIC_ASSERT(convert(7) == 8);
  STATIC_ASSERT(convert(8) == -1);
}

所以:

  • 是什么促使禁止在 constexpr 函数中使用静态存储变量?

  • 是否有我可能错过的更清洁的替代方案?当然,我可以从 convert 中提取 table,但我想避免这种情况。

  • 标准是否保证非 constexpr 上下文中 constexpr 函数中的 const 数组将位于静态存储而不是堆栈中?

最佳答案

编辑:我之前的回答完全错误...所以我要改正!

我知道我来晚了,我相信你已经想出了我要说的一切。

不幸的是,您不能在 constexpr 函数中包含静态变量。

C++14 标准规定 constexpr 函数体不能包含“...static 或线程存储持续时间的变量的定义”。这是有道理的,因为 constexpr 上下文是在那些运行时 构造之外评估的。

但是我们使用的是 C++ 而不是 C,这意味着我们有对象!对象可以有 constexpr 静态存储!我的解决方案是将函数(作为静态函数)包装在一个对象/类型中,该对象/类型包含作为 constexpr 静态成员的数据。

我知道静态变量会导致非 constexpr 世界中的初始化问题,因此您可能需要使用预处理器根据您正在编译的标准选择工作版本。

这样,C++14 代码就变成了这样:

class convert {
  static constexpr int table[] = {3, 2, 6, 1, 7, 1, 6, 8};
public:
  static constexpr int run(int v) {
    if (0 <= v && v < sizeof table / sizeof table[0])
      return table[v];
    else
      return -1;
  }
};

您可能还想添加一个 static constexpr int/unsigned int/size_t 来指定数组的长度,以删除丑陋的 sizeof 您必须做的事情(尽管如果您针对多个标准,那么这可能不可行)。

您可以改为将其指定为模板参数,(我相信)它应该适用于所有标准!

关于你最后一个问题。我很确定 constexpr 函数内的任何非静态 constexpr 对象都将在非 constexpr 上下文中的堆栈上初始化(在 Compiler Explorer 上进行的一些测试似乎与此一致)。主要区别在于,结果值将在一开始就构建,“无”运行时成本(constexpr 存在的原因)。

我觉得这比其他答案更接近您原来的答案,模板有时会让您头疼(尽管我个人会使用可变参数模板方法来生成数组)

参见 https://en.cppreference.com/w/cpp/language/constexprDoes static constexpr variable inside a function make sense?了解更多信息!

关于c++ - constexpr 函数中的静态表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53941245/

相关文章:

C++ 堆栈和二维数组

c++ - 外部数组作为非类型模板参数与 clang c++1z

c++ - 如果字符串有空格,则引发编译时错误

c++ - 使用 .erase (C++) 的逻辑错误

c++ - QT中动态添加QObject的SIGNAL(带参数)如何处理?

c++ - 如何检查 std::variant 是否可以保存某种类型

C++ - 将 double vector 作为元组迭代

c++ - 编译时内联内存分配

c++ - 将 constexpr 编译字符串转换为模板字符列表

c++ - 将自定义运算符 < 与 std::less 一起使用时出错