【问题标题】:constexpr function with extern static table带有外部静态表的 constexpr 函数
【发布时间】:2017-04-04 00:10:51
【问题描述】:

有类似的问题,但我发现没有一个可以直接回答这个问题。

我想实现一个这样的 constexpr 函数:

constexpr int Foo(int x) {
  static const int table[128] = { 3, 1, 4, 1, 5, ..., 99 };
  return (0 <= x && x < 128) ? table[x] : 42;
}

我对将表设为静态函数持谨慎态度,因为编译器可能会添加昂贵的检查以使表线程的初始化安全(减慢每次调用),并且这些检查可能会使优化器不太可能内联它,否则微不足道的功能。

所以我想我会将表移动到命名空间静态,在一个 .cpp 文件中定义它,而函数本身仍然定义在标题中,以便可以内联。

constexpr int Foo(int x) {
  extern constexpr int table[];
  return (0 <= x && x < 128) ? table[x] : 42;
}

编译器抱怨我不能在 constexpr 函数中声明 table。于是我尝试了:

extern constexpr int table[];
constexpr int Foo(int x) {
  return (0 <= x && x < 128) ? table[x] : 42;
}

这是不允许的,因为你不能只声明一些 constexpr,你必须定义它。但是如果我在头文件中定义表,我就违反了单定义规则,对吧?

constexpr int table[128] = { 3, 1, 4, 1, 5, ..., 99 };
constexpr int Foo(int x) {
  return (0 <= x && x < 128) ? table[x] : 42;
}

我知道 Foo 没有违反 ODR,因为 constexpr 意味着函数定义的内联。编译器接受这一点并且似乎做了正确的事情,但我知道编译器不需要针对 ODR 违规发出诊断。

Q1:在最后一次迭代中,table 是否违反了 ODR?

Q2:如果没有,有没有办法防止 table 对包含此标头的每个翻译单元可见?

【问题讨论】:

  • "我很担心将表格设为静态函数,因为..." 由于@987654321,这对ints 来说根本不是一个现实的问题@.
  • @ildjarn:很公平,但这个问题还有其他动机。我的编译器 (VC++2015) 不允许我在 constexpr 函数中将表声明为静态函数,我认为这需要符合 C++14。
  • 幸运的是,VC++2017 至少会。 :-]
  • 关于 Q1,constexpr 暗示 const 暗示 static,所以是的,这是违反 ODR 的。在 C++17 中,它可以被标记为 inline 来解决这个问题。

标签: c++ c++11 constexpr one-definition-rule


【解决方案1】:

不确定Q1(如果你定义tablestatic?)但是,对于Q2,我建议Foo()作为friend函数用于tableprivatestatic constexpr的类会员。

举例

#include <iostream>

class wrapTable
 {
   private:
      static constexpr int table[] { 2, 3, 5, 7, 11, 13, 17, 19 };
      static constexpr int size { sizeof(table)/sizeof(table[0]) };

      friend constexpr int foo (int);
 };

constexpr int wrapTable::table[];

constexpr int foo (int x)
 { return (0 <= x && x < wrapTable::size) ? wrapTable::table[x] : 42; }

int main()
 {
   std::cout << "foo(3): " << foo(3) << std::endl; // print 7
   std::cout << "foo(9): " << foo(9) << std::endl; // print 42

   // compilation error: 'table' is a private member of 'wrapTable'
   // std::cout << "table(3): " << wrapTable::table[3] << std::endl;
 }

【讨论】:

  • 有趣的想法。我的编译器不接受 constexpr int wrapTable::table[]; 行。即使它起作用了,它仍然会污染每个消费者的命名空间,只是用wrapTable 而不是table
  • @AdrianMcCarthy - 你用的是哪个编译器?
  • @AdrianMcCarthy - 这比我的 g++ 4.9.2 或 clang++ 3.5.0 更新...我的代码错了吗?哪个消息错误显示了您的 VC++?
  • @AdrianMcCarthy:将static constexpr int table[] { 2, 3, 5, 7, 11, 13, 17, 19 }; 更改为static constexpr int table[8] { 2, 3, 5, 7, 11, 13, 17, 19 };,它应该在VC++ 2015 中编译良好。
猜你喜欢
  • 2019-05-25
  • 2016-07-29
  • 2020-10-08
  • 1970-01-01
  • 2019-10-03
  • 2019-07-05
  • 2012-07-16
  • 1970-01-01
  • 2012-12-01
相关资源
最近更新 更多