在 C++17 之前的模式下无法使用静态 constexpr 进行编译
Can't compile with static constexpr under pre-C++17 mode
为什么下面的最小示例不使用c++11
或c++14
编译,而是以c++17
和c++2a
编译?
#include <iostream>
#include <limits>
#include <vector>
// works:
// static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();
class Classy {
// does not work in c++11 (constexpr introduced) nor c++14:
// works if c++17 or newer:
static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();
int VALUE_LIMIT_B = std::numeric_limits<int>::max();
public:
explicit Classy();
std::vector<int> classy;
};
Classy::Classy() {
// does not work:
classy.resize(3, VALUE_LIMIT_A);
// works:
// classy.resize(3, std::numeric_limits<int>::max());
// works:
// std::cout << VALUE_LIMIT_A;
// works:
// classy.resize(3, VALUE_LIMIT_B);
}
// required in c++11 and c++14
// constexpr int Classy::VALUE_LIMIT_A;
int main() {
Classy classy{};
for (const auto& elem : classy.classy) {
std::cout << elem << ",";
}
std::cout << "n";
}
这是带有c++11
的输出:
$ g++ -std=c++11 main.cpp && ./a.out
/tmp/ccon7pPo.o: In function `Classy::Classy()':
main.cpp:(.text+0x31): undefined reference to `Classy::VALUE_LIMIT_A'
collect2: error: ld returned 1 exit status
这是带有c++17
的输出:
$ g++ -std=c++17 main.cpp && ./a.out
2147483647,2147483647,2147483647,
因为从 C++17 开始,不再需要在命名空间范围内定义 constexpr 静态数据成员。
如果使用 odr
non-inline (since C++17)
静态数据成员or a constexpr static data member (since C++11)
的 const 定义 在命名空间范围内仍然是必需的,但它不能具有 初始 化。This definition is deprecated for constexpr data members (since C++17).
struct X { static const int n = 1; static constexpr int m = 4; }; const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used const int X::n; // … so a definition is necessary constexpr int X::m; // … (except for X::m in C++17)
如果一个静态数据成员被声明为
constexpr
,则隐式inline
并且不需要在命名空间范围内重新声明。这 不带初始值设定项的重新声明(以前如图所示需要 以上)仍允许,但已弃用。(自C++17起)
请注意,std::vector::resize
通过引用获取第二个参数; 这导致VALUE_LIMIT_A
用于classy.resize(3, VALUE_LIMIT_A);
的 odr 。
自 C++17 年以来,随着内联变量的引入,static constexpr
数据成员都是隐式内联变量:
[dcl.constexpr]
1 ...使用 constexpr 声明的函数或静态数据成员 说明符隐式是内联函数或变量 ([dcl.inline])...
内联变量与内联函数一样,在它们使用的每个翻译单元中定义。编译器将多个定义解析为一个定义。这意味着,与 C++14 不同,无需为了 ODR 而为static constexpr
变量显式提供类外定义,编译器会处理它。
您也可以在 C++14 中侥幸逃脱。像提到的其他答案一样,ODR-使用静态数据成员resize
。不过,您可以解决它:
classy.resize(3, int(VALUE_LIMIT_A));
虽然它看起来是多余的,但实际上它与直接使用常量具有不同的行为。这将创建一个具有常量值的临时整数。但它不使用 ODR 常量。临时绑定到引用,因此避免了问题。虽然最好在 C++17 之前的代码中定义常量,但您可以使用此技巧来调整您无法控制的代码。
- 添加静态constexpr成员是否会更改结构/类的内存映射
- 类内部和外部静态 constexpr 元组之间的差异
- 静态 constexpr 类成员变量对多线程读取是否安全?
- Clang 8 带有静态 constexpr 和数组的链接器错误 - 错误是什么以及如何解决它?
- constexpr函数中的静态constexpr变量
- 类无法访问自己的私有静态 constexpr 方法 - Clang bug?
- C++访问静态 constexpr 数组
- 初始化模板化类中的静态 constexpr 成员
- 静态 constexpr 成员变量初始化
- 是否使用静态 constexpr 变量 odr?
- 静态 constexpr 全局变量
- 使用静态 constexpr 成员的未解析外部符号
- 在模板定义中调用非静态constexpr成员函数
- 类本身内部的类对象的静态constexpr数组
- 为什么非成员静态 constexpr 变量不是隐式内联的?
- 类中的静态 constexpr 初始化链
- 静态 constexpr 的编译器优化
- 无法声明静态 constexpr char []
- 为什么 clang 并不总是为相同的静态 constexpr 产生常量值
- emplace_back会导致静态 constexpr 成员上出现链接错误