【发布时间】: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