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

时间:2019-09-23 15:32:43

标签: c++ language-lawyer c++17 one-definition-rule

即使阅读了标头中定义的this question about non explicit inline namespace scoped variables,由于对ADR的AFAIK违规行为都是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

2 个答案:

答案 0 :(得分:1)

好吧...由于您实际上设法使自己对您的问题感到困惑,所以我想我应该进一步研究它。首先,我们必须对变量的相关属性进行分类:生命周期,可见性,链接 这些受关键字staticinlineconstexprconstextern的影响。

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

因此,
-global_non_expl_inline:默认为外部链接。没问题,除非另一个翻译单元使用外部链接定义了另一个这样的变量。
-global_non_expl_inline_static:内部链接。很好,只要您不在任何地方定义其他此类变量即可。
-global_expl_inline:外部链接和inline。没问题,除非另一个翻译单元在没有inline的情况下声明了另一个这样的变量。
-global_expl_inline_explicit_static:很好,如果您不希望static inline变量在链接时可用,但又希望在所有翻译单元中使用相同的变量-例如,对所有类型的变量都有用常数。
-global_expl_inline_explicit_extern:外部链接和inline。没问题,除非另一个翻译单元在没有inline的情况下声明了另一个这样的变量。
-global_expl_inline_explicit_extern_but_unnamed_ns:根据cppreference的内部链接。

在课程范围内:
-in_class_static:外部链接。根据{{​​3}},很好,但是如果使用了odr,则需要在名称空间范围内进行声明。
-in_class_but_out_of_source_def:外部链接。还可以这实际上是标准方法。

总而言之,未定义的行为比您似乎想的要少得多-很好。但是,有几件事是有效的,但在未命名的命名空间中却没有extern的意义。

关于您对此问题的评论:我无法重现该问题,该问题的评论部分中的其他人也不会。您还可以在其评论部分找到该问题的其他合理性问题。请记住,有关堆栈溢出的一些问题是由不完全知道遇到问题时应该采取哪些步骤的人提出的。对于这个特定的问题,我不会太在意;)

答案 1 :(得分:1)

(由于该问题的标签为C ++ 17,因此我将使用标准N4659草案作为参考,以避免模块引起的复杂性。)

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

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

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

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

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

  • 都是global_non_expl_inline(这是一种非常量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中,但没有定义就无法使用。)