在 C++17 之前的模式下无法使用静态 constexpr 进行编译

Can't compile with static constexpr under pre-C++17 mode

本文关键字:静态 constexpr 编译 C++17 模式      更新时间:2023-10-16

为什么下面的最小示例不使用c++11c++14编译,而是以c++17c++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 静态数据成员。

如果使用 odrnon-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 之前的代码中定义常量,但您可以使用此技巧来调整您无法控制的代码。