c++11:为啥静态 constexpr 的类内初始化不是定义?

Posted

技术标签:

【中文标题】c++11:为啥静态 constexpr 的类内初始化不是定义?【英文标题】:c++11: Why a in-class initialization of a static constexpr not a definition?c++11:为什么静态 constexpr 的类内初始化不是定义? 【发布时间】:2021-09-14 01:56:16 【问题描述】:

考虑以下简单:

#ifndef TEST_H
#define TEST_H

class Test 
public:
    static constexpr int a = 1;


#endif

注意:

    没有因宏而违反 ODR。 为什么 constexpr static int a 不被视为定义,因为它是在类 Test 中定义的?因为它不是一个定义,因此它需要类之外的以下内容。为什么?

constexpr int Test::a;

【问题讨论】:

自 C++17 起不再需要此项。 在内联变量之前,所有静态成员变量都必须在单个 TU 中定义,以避免破坏 ODR。 C++17 引入了内联变量,让这个问题迎刃而解。 详细解释见softwareengineering.stackexchange.com/questions/145299。 “由于宏没有 ODR 违规。” - 包含守卫不能防止 ODR 违规。它们保护单个 TU 不被重新纳入,但 ODR 适用于整个计划及其所有 TU! 请看下面我对杰瑞的问题。发送 【参考方案1】:

为什么静态 constexpr 的类内初始化不是定义?

因为一个定义规则 (ODR)。该规则规定,每个非内联非成员变量和静态成员变量都必须有一个定义。由于其性质,类定义通常包含在多个翻译单元中。如果类定义包含变量定义,则包含在多个翻译单元中将违反 ODR。

从 C++17 开始,该语言具有内联变量,因此您可以在类定义中定义此类内联变量。

【讨论】:

我了解 ODR,但这是我的问题。如果我们如上所述在该文件本身中有宏#define,为什么上述 constexpr 静态数据成员会违反 ODR?认为多个文件可能包含这个类.. 总会有 1 个 Test 类定义。 @yapkm01 如果您编译 g++ -c a.cpp; g++ -c b.cpp; g++ a.o b.o 并且 a.cpp 和 b.cpp 都包含您的标头,则包含保护不会阻止类定义出现在可执行文件中两次,因为每个 cpp 文件都是单独编译的所以每个人都必须包含这个类.. 如果 a.cpp 和 b.cpp 都包含具有类 Test 的标头,并且正如您所说的标头保护适用于每个 TU,那么如何链接它们将满足 ODR,因为 a.o 和 b.o 都包含类 Test ? @yapkm01 因为 ODR 允许在每个 TU 中定义类,前提是定义相同。 如果将相同的类视为定义,为什么跨 TU 允许相同的类。就像 int a = 1;定义不能跨 TU 重复?除非你说在这个阶段不涉及存储(?)类中的 constexpr 静态成员呢?他们不被认为是定义吗?看起来不像,因为编译器只是使用它们来替换需要的地方(?)

以上是关于c++11:为啥静态 constexpr 的类内初始化不是定义?的主要内容,如果未能解决你的问题,请参考以下文章

静态常量整数数组

为啥这个 constexpr 静态成员函数在调用时不被视为 constexpr?

请解释 constexpr [重复]

为啥 C++11 类内初始化程序不能使用括号?

具有非静态成员初始化器的类的 C++11 聚合初始化

如何初始化std :: vector的静态constexpr成员 在c ++ 11中?