【问题标题】:constexpr variable at namespace scope without explicit inline definition and with命名空间范围内的 constexpr 变量,没有显式内联定义,并且
【发布时间】:2019-09-23 15:32:43
【问题描述】:

即使在阅读了标题中定义的this question about non explicit inline namespace scoped variables 之后,我还是有点偏执于明确的内联命名空间范围变量是可以的,因为针对 ODR 的 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

【问题讨论】:

  • constexpr int global_non_expl_inline = 42; 为什么这个和其他会是 UB?除了extern,这些项目似乎都很好。
  • “甚至 cppreference.com 都自相矛盾”。哪些页面?
  • 我认为你链接的问题解释了它。它们是具有内部链接的内联变量,如果您有一个内联函数绑定从不同翻译单元调用的对它们的引用,那么这个问题可能会困扰您。您能否更具体地说明您的问题的性质?
  • @Jarod42 en.cppreference.com/w/cpp/language/inline with "内联函数或内联变量 (C++17 起) 具有以下属性:" vs en.cppreference.com/w/cpp/language/definition " 内联变量与外部链接 (C++17 起) ...只要满足以下所有条件:"
  • @VTT 因为它是一个非内联非易失性 const 限定变量,它具有内部链接并且违反了 ODR?另见stackoverflow.com/a/46107877/3537677

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


【解决方案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:外部联动。好的,根据cppreference,但如果它是 odr-used,则需要在命名空间范围内声明。
- in_class_but_out_of_source_def:外部链接。也很好。这其实是标准的方式。

总之,未定义的行为比您想象的要少得多——这很好。然而,在未命名的命名空间中,有些东西是有效的,但并不像 extern 这样真正有意义。

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

【讨论】:

  • 你从哪里得到“constexpr: 意味着 const 并且如果指定了静态也意味着内联”? cppreference.com 说“声明为 constexpr 的静态成员变量(但不是命名空间范围变量)隐含地是一个内联变量。”
  • 连同前面的句子“对象声明中使用的 constexpr 说明符 [或非静态成员函数(C++14 前)] 暗示 const。函数中使用的 constexpr 说明符 [或静态成员变量 (C++17 起)] 声明意味着内联。"
  • 你从哪里得到“不是命名空间范围”的东西?
  • 现在应该修复
  • eel.is/c++draft/basic.def.odr#12.3 之后,在我看来仍然只有外部链接是可以的。
【解决方案2】:

(由于题目标记为C++17,我将使用标准草案N4659作为参考,避免了模块带来的复杂性。)

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

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

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

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

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

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

  • global_expl_inline
  • global_expl_inline_explicit_extern

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

  • bar::in_class_but_out_of_source_def

bar::in_class_static没有定义,可以出现在多个TU中,但没有定义就不能odr-used。)

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2018-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 2013-05-30
相关资源
最近更新 更多