考虑以下标头,并假定它已在多个TU中使用:
static int x = 0;
struct A {
A() {
++x;
printf("%d\n", x);
}
};
正如this question所述,这是违反ODR的行为,因此是UB。
现在,there is no ODR violation如果我们的inline
函数引用的是非volatile
const
对象,并且我们不打算在该函数中使用它(以及其他规定) ),因此在标题中仍然可以正常使用
constexpr int x = 1;
struct A {
A() {
printf("%d\n", x);
}
};
但是,如果确实要使用它,我们将与UB一起回到第一个方框:
constexpr int x = 1;
struct A {
A() {
printf("%p\n", &x);
}
};
因此,鉴于我们现在有了inline
个变量,指南是否应该在标头中将所有namespace
范围内的变量标记为inline
以避免所有问题?
constexpr inline int x = 1;
struct A {
A() {
printf("%p\n", &x);
}
};
这似乎也更容易讲授,因为我们可以简单地说出“ inline
-标头中的所有内容”(即函数和变量定义)以及“从不标头中的static
”。 / p>
这个推理正确吗?如果是,将标头中的const
和constexpr
变量始终标记为inline
是否有任何不利之处?
答案 0 :(得分:6)
正如您所指出的,根据[basic.def.odr]/12.2.1
,示例一和示例三确实违反了ODR。在D的每个定义中的[..] ,根据[basic.lookup]查找的对应名称,应指代D定义中定义的实体,在重载解析之后和部分模板专门化匹配之后,引用同一实体,除了名称可以引用
具有内部链接或不具有链接的非易失性const对象,如果该对象
D的任何定义中均未使用
- , [..]
这个推理正确吗?
是的,只要所有定义都相同,即使具有 odr-used 的功能,也可以保证具有外部链接的内联变量引用相同的实体:
内联函数或变量应在使用过的每个转换单元中定义,并且在每种情况下均应具有完全相同的定义([basic.def.odr])。 [..] 具有外部链接的内联函数或变量在所有翻译单元中应具有相同的地址。
最后一个示例是可以的,因为它满足并且没有违反上面的粗体部分。
总是将标头中的const和constexpr变量标记为内联有什么缺点吗?
我想不出什么,因为如果我们保证通过TU的内联变量具有与外部链接完全相同的定义,那么编译器可以自由选择它们中的任何一个来引用该变量,这将从技术上讲,是相同的,因为只有一个TU,并且在标头中声明了具有适当标头保护的全局变量