提问人:Oersted 提问时间:11/15/2023 更新时间:11/15/2023 访问量:43
类内与类外静态成员初始化:const(expr)ness 和 ODR
In-class vs out-of-class static member initialization: const(expr)ness and ODR
问:
在实验上,我知道如何声明/初始化/定义各种类型的数据成员(主要是通过阅读编译器诊断),但我意识到我并不真正理解这些机制和单一定义规则背后的规则,但我在理解数据成员初始化逻辑和尊重 ODR 方面存在问题。
我的困惑因行为的差异而增加 -ness, -ness。
我在 SO 上找到了几篇关于数据成员和 ODR 的帖子,但我不确定它们是否准确地解决了我在下面的问题。至少,我读到的任何东西都不能给我一个大局,都在一个地方。static
static
const
constexpr
static
https://en.cppreference.com/w/cpp/language/static 提供了有关数据成员的规则,但我并不完全理解有关单一定义规则的规则。static
假设我有以下文件。
类ODR.h
struct CData {
#ifdef IN_CLASS_DEFINITION
static int m_Value = 3;
#else
static int m_Value;
#endif
#ifdef IN_CLASS_CONST_DEFINITION
static int const m_kValue = 3;
#else
static int const m_kValue;
#endif
#ifdef IN_CLASS_CONSTEXPR_DEFINITION
static double constexpr m_kfValue = 3.14;
#else
static double constexpr m_kfValue;
#endif
};
#ifdef OUT_OF_CLASS_IN_HEADER_DEFINITION
int CData::m_Value = 3;
#endif
#ifdef OUT_OF_CLASS_CONST_IN_HEADER_DEFINITION
int const CData::m_kValue = 3;
#endif
#ifdef OUT_OF_CLASS_CONSTEXPR_IN_HEADER_DEFINITION
constexpr double m_kfValue = 3.14;
#endif
类ODR.cpp
#ifndef OUT_OF_CLASS_IN_HEADER_DEFINITION
int CData::m_Value = 3;
#endif
#ifndef IN_CLASS_CONST_DEFINITION
#ifndef OUT_OF_CLASS_CONST_IN_HEADER_DEFINITION
int const CData::m_kValue = 3;
#endif
#endif
#ifndef IN_CLASS_CONSTEXPR_DEFINITION
#ifndef OUT_OF_CLASS_CONSTEXPR_IN_HEADER_DEFINITION
int const CData::m_kfValue = 3.14;
#endif
#endif
OUT_OF_whatever_IN_HEADER_DEFINITION
只有在不是时才能定义。IN_whatever_DEFINITION
测试ODRUse#N.cpp
void TestODRUse#N() {
std::cout << CData::m_Value << '\n';
std::cout << CData::m_kValue << '\n';
std::cout << CData::m_kfValue << '\n';
}
main.cpp
int main() {
TestODRUse1();
TestODRUse2(); // testing for ODR violations
}
为了清楚起见,特意省略了其他琐碎的头文件和指令,这些文件和指令需要作为不同文件之间的粘合剂。
此处提供了完整的示例。#include
这些是一些意见(为了记录和详尽无遗)和一些问题。
1- 如果设置了任何变量,则存在 ODR 违规:在 TestODR1 和 TestODR2 转换单元中定义了相应的变量。毫无疑问。此外,可以通过从 C++17 开始声明变量来缓解这种情况。OUT_OF_whatever_IN_HEADER_DEFINITION
inline
2- 我想知道为什么非成员不能内联初始化,我找到了那个答案,解释这是为了避免违反 ODR:https://stackoverflow.com/a/61519186static
const
3- 但另一方面,一旦数据被声明,情况就会发生变化。
成员可以(必须)内联初始化:因此每个翻译单元都看到一个定义,但没有违反 ODR:为什么?const(expr)
static const(expr)
5- 最终,来自上面链接的 cppreference 页面的以下段落,关于数据成员,让我感到困惑:constexpr static
如果使用 odr 的 const 非内联(自 C++17 起)静态数据成员或 constexpr 静态数据成员(自 C++11 起)(直到 C++17),则仍需要命名空间范围内的定义,但它不能具有初始值设定项。 constexpr 的命名空间范围 给出了以下示例:
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)
然而,我认为我从来不需要重新定义类外初始化的数据成员。static constexpr
总而言之,我想根据数据成员的常量(expr)来澄清一下 ODR。感谢指定不同 C++ 版本可能存在的差异。static
答: 暂无答案
评论