命名空间范围内的 constexpr 变量,没有显式内联定义,并且有

constexpr variable at namespace scope without explicit inline definition and with

即使在阅读了 中定义的 headers 之后,我仍然对显式内联命名空间范围变量是否正常感到有点偏执,因为 AFAIK 违反 ODR 是 UB,不需要诊断。我的理解是否正确,在命名空间范围内显式内联指定的 constexpr(和 const 非易失性类似)定义的变量是 内联变量 ,因此它们的 ODR 使用是可以的用于不同的翻译单元? 甚至 cppreference.com 也自相矛盾,有时它说内联变量必须是外部的 ODR 使用异常,而在另一个页面上,通常只有内部链接内联变量是可以的,只有在有额外要求的情况下才是外部的。

基本上这些假设是正确的吗?:

/*! @file some_header.hpp */
#ifndef HEADER_GUARD
#define HEADER_GUARD

constexpr int global_non_expl_inline = 42; //UB
static constexpr int global_non_expl_inline_static = 42; //UB
inline int global_expl_inline = 42; //ok
inline int static global_expl_inline_explicit_static = 42; //? external linkage by default but static explicit but still ok?
inline int extern global_expl_inline_explicit_extern = 42; //UB

namespace foo {
constexpr int global_non_expl_inline = 42; //UB
static constexpr int global_non_expl_inline_static = 42; //UB
inline int global_expl_inline = 42; //ok
inline int static global_expl_inline_explicit_static = 42; //? external linkage by default but static explicit but still ok?
inline int extern global_expl_inline_explicit_extern = 42; //UB
}

namespace {
inline int extern global_expl_inline_explicit_extern_but_unnamed_ns = 42; //ok
}

struct bar{
    static int const in_class_static = 42;//ok
    static int in_class_but_out_of_source_def;
};

int bar::in_class_but_out_of_source_def = 42;//UB

#endif

嗯...既然你真的设法让我自己对你的问题感到困惑,我想我会进一步研究它。首先,我们要对一个变量的相关属性进行分类:lifetime,visibility,linkage 这些受关键字的影响:您在问题中使用的 staticinlineconstexprconstextern

在变量定义的命名空间范围内:
- static: 指定内部 linkage
- inline:允许在不同的翻译单元中对同一变量进行多个相同的定义,并确保它们将引用同一对象(例如具有相同的地址)
- constexpr: 表示 const - const: 默认为外部 linkage
- extern: 指定外部 linkage

因此,
- global_non_expl_inline:默认为外部 linkage。没问题,除非另一个翻译单元用外部 linkage.
定义另一个这样的变量 - global_non_expl_inline_static:内部 link 年龄。可以,只要您不在任何地方定义其他此类变量即可。
- global_expl_inline:外部 link 年龄和 inline。没问题,除非另一个翻译单元在没有 inline.
的情况下声明另一个这样的变量 - global_expl_inline_explicit_static: 很好,如果你不希望它在 link 时间可用,那么 static inline 变量是有意义的,但确实希望在所有翻译单元中使用相同的变量 - 例如有用对于各种常量。
- global_expl_inline_explicit_extern:外部 link 年龄和 inline。没问题,除非另一个翻译单元在没有 inline.
的情况下声明另一个这样的变量 - global_expl_inline_explicit_extern_but_unnamed_ns:内部 link年龄根据 cppreference

在 class 范围内:
- in_class_static:外部 link 年龄。很好,根据 cppreference,但如果它是 odr-used,则需要在名称空间范围内声明。
- in_class_but_out_of_source_def:外部 link 年龄。还可以。这实际上是标准方式。

总而言之,未定义的行为比您想象的要少(很多)- 这很好。然而,有一些东西是有效的,但没有真正意义,例如未命名名称空间中的 extern

关于您对此问题的评论:我无法重现该问题,该问题评论部分的其他人也无法重现。您可以在其评论部分找到该问题的其他合理性问题。请记住,一些关于 Whosebug 的问题是由不确切知道当 运行 遇到问题 时他们采取了哪些步骤的人提出的。我不会太在意那个特定的问题 ;)

(由于题目是C++17,所以参考标准草案N4659,避免模块复杂化)

首先,请注意不同翻译单元中的名称只有在具有外部链接时才指代同一实体 ([basic.link]/9)。具有内部链接的名称始终指代不同翻译单元中的不同实体,即使它们的定义看起来相同。

因此,我们可以将这些定义分为三组:

  1. 内部链接
    • 可以出现在多个翻译单元中(不是UB);会在不同的TU
    • 定义不同的变量
  2. 与内联说明符的外部链接
    • 可以出现在多个翻译单元中(不是UB);将在所有 TU
    • 中定义相同的变量
  3. 没有内联说明符的外部链接
    • 不能出现在多个翻译单元中(UB:ODR 违规)

(如果变量在具有外部链接的另一个定义中 ODR 使用,则第一组中的定义可能会出现问题,这可能违反 [basic.def.odr]/(6.2)。)

以下定义属于第一组(内部链接):

  • 两者都是global_non_expl_inline(它是一个非volatile const限定类型的非内联变量,并且之前没有声明。因此它匹配[basic.link]/(3.2)
  • 两者都 global_non_expl_inline_static ([basic.link]/(3.1))
  • 两者都 global_expl_inline_explicit_static ([basic.link]/(3.1))
  • global_expl_inline_explicit_extern_but_unnamed_ns ([basic.link]/(4.1))

以下定义属于第二组(带内联说明符的外部链接):

  • 两个global_expl_inline
  • 两个global_expl_inline_explicit_extern

以下定义属于第三组(不带内联说明符的外部链接):

  • bar::in_class_but_out_of_source_def

(bar::in_class_static未定义,可以出现在多个TU中,但没有定义不能使用。)