为什么非成员静态 constexpr 变量不是隐式内联的?

问题描述 投票:0回答:2

在 C++17 中,我们得到了内联变量,并且我假设全局 constexpr 变量是隐式内联的。 但显然这只适用于静态 member 变量。

这背后的逻辑/技术限制是什么?

来源:

声明为 constexpr 的静态成员变量(但不是名称空间范围变量)隐式是内联变量。

c++ static c++17 inline inline-variable
2个回答
10
投票

这里的要点是,命名空间范围内的

constexpr int x = 1;
在 C++14 中具有内部链接。

如果在不更改内部链接部分的情况下使其隐式内联,则更改不会产生任何效果,因为内部链接意味着无论如何都无法在其他翻译单元中定义它。这会损害可教性,因为我们希望像

inline constexpr int x = 1;
这样的东西默认获得外部链接(毕竟,内联的全部目的是允许在多个翻译单元中定义 same 变量)。

如果你让它隐式地内联于外部链接,那么你就破坏了现有的代码:

// TU1
constexpr int x = 1;

// TU2
constexpr int x = 2;

这个完全有效的 C++14 将成为 ODR 违规。


10
投票

隐式创建

constexpr
静态数据成员的原因
inline
是为了解决 C++ 中的一个常见问题:在定义类范围常量时,之前必须在一个翻译单元中发出定义,以免变量使用 ODR:

// foo.h
struct foo {
    static constexpr int kAnswer = 42;
};

// foo.cpp
// a linker error will occur if this definition is omitted before C++17
#include "foo.h"
constexpr int foo::kAnswer;

// main.cpp
#include "foo.h"
#include <vector>
int main() {
    std::vector<int> bar;
    bar.push_back(foo::kAnswer);  // ODR-use of 42
}

在这种情况下,我们通常只关心常量的值,而不关心它的地址;如果确实使用了 ODR,编译器可以很方便地为常量合成一个唯一的位置,但我们不关心该位置在哪里。

因此,C++17 改变了规则,不再需要外线定义。为此,它使

foo::kAnswer
的声明成为内联定义,以便它可以出现在多个翻译单元中而不会发生冲突,就像内联函数一样。

对于 namespace-scope

constexpr
变量(隐式
static
,因此具有 internal 链接,除非声明
extern
)不存在类似问题。每个翻译单元都有自己的副本。目前指定的
inline
对此类变量没有影响。改变现有的行为会破坏现有的程序。

© www.soinside.com 2019 - 2024. All rights reserved.