【问题标题】:Confusion about declaration and definition of static const data memebers关于静态 const 数据成员的声明和定义的困惑
【发布时间】:2020-05-17 18:11:05
【问题描述】:

Scott Meyers 在 Effective Modern C++,Item 30 page 210 中写道,有

无需在类中定义整数static const数据成员;仅声明就足够了,

那么示例代码是

class Widget {
  public:
    static const std::size_t MinVals = 28; // MinVals' declaration;
    ...
};
...                                        // no defn. for MinVals
std::vector<int> widgetData;
widgetData.reserve(Widget::MinVals);       // use of MinVals

我确信static const std::size_t MinVals = 28; 是声明也是定义,因为它为MinVals 赋予了价值,但评论似乎声称这只是声明;第二条评论实际上声称没有定义。代码后面的文字,确实是这样的

MinVals 缺少定义。

这证实了static const std::size_t MinVals = 28; 不是一个定义,所以我有点困惑。

cppreference 对我没有多大帮助(我的粗斜体):

如果整数或枚举类型的static 数据成员声明 const(而不是volatile),它可以用初始化器在其中每个表达式都是一个常量表达式,就在类定义中:

struct X
{
   const static int n = 1;
   const static int m{2}; // since C++11
   const static int k;
};
const int X::k = 3;

但是类中的前两行在我看来是定义。

以下关于 cppreference 的示例也是如此:

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 const int n = 1; 是一个定义,但它不是,基于倒数第二个评论。

【问题讨论】:

  • 来自 n4835.pdf [class.static.data] (2) The declaration of a non-inline static data member in its class definition is not a definition [...]。但是 (3) 直接用于具有初始化程序的整数类型,如果这是声明或定义,则本段没有帮助。
  • 再想一想,@AndyG,我没有其他要明确说明的内容来明确我的问题。标题以Confusion about开头。而已。我对此感到困惑,我寻求帮助。 对 [...] 的困惑。你能帮我理解一下吗?
  • C++17引入inline static,厉害了。
  • 事后看来,this 也有关系。

标签: c++ static language-lawyer variable-declaration


【解决方案1】:

无需在类中定义完整的静态 const 数据成员;仅声明就足够了,

仅当该对象不是ODR-used 时,单独声明就足够了,也就是说,如果数据成员未在需要其地址存在的上下文中使用(例如绑定到引用或应用运算符&amp;)。初始化器的存在等于定义。

在书中的示例中,很明显MinVals 不是 ODR 使用的,即编译器可以直接使用它的值,而不必在内存中创建对象,因此声明:

widgetData.reserve(Widget::MinVals);

变成:

widgetData.reserve(28);

但是,如果在任何其他地方,MinVals 被 ODR 使用,那将导致程序格式错误。

来自 cppreference 的所有其他示例都清楚地表明了何时使用 ODR 并需要定义,何时不需要:

struct X
{
    const static int n = 1;
    const static int m{2}; // since C++11
    const static int k;
};
const int X::k = 3;

nm 是带有初始值设定项的声明。尝试获取nm 的地址应该会失败。

struct X {
    static const int n = 1;
    static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m;
const int X::n;
constexpr int X::m;

表达式&amp;X::n&amp;X::m 分别算作nm 的ODR 使用(即请求地址)。对于 constexpr 静态数据成员,在 C++17 之前需要定义。从 C++17 开始,static constexpr 数据成员隐含为 inline,这意味着不需要类外定义,因为它们本身就是定义。

【讨论】:

  • 这里的关键短语是initializing declaration
  • 答案已接受,但我对您答案中的第一段有两个疑问。只有当 [...]; 时,您才编写 声明就足够了;您是专门指_integral static const data members_的声明,还是一般的声明?您以初始化程序的存在不等于定义结束该段落,这使它看起来像是它之前的结果,但我看不到连接。也许你可以澄清第一段?
  • 这取决于您询问的声明类型。您可以随意不定义静态数据成员(无论是否为 const),只要您不使用 ODR 就可以声明它。你可以随意不定义一个函数,只要它没有被调用。您可以随意不定义一个类,只要您不在需要其完整定义存在的上下文中使用它。您可以将初始化程序放入整体 const 静态数据成员,但它不会使其成为定义。它是有效的,并且允许编译器执行优化,只要该实体不被 ODR 使用。
  • 不要只关注语法。 static const int i = 0; 意味着不同的东西,无论是在类范围内还是在全局范围内。最终重要的是语义,它来自 syntaxcontext
  • 并且该标准在[basic.def]/p2.3 中明确将其定义为非定义,除非您还添加了inline 关键字。
【解决方案2】:

查看此Draft Standard,您的示例似乎属于灰色区域。虽然没有明确提及以下行:

    static const std::size_t MinVals = 28;

给出了一个非常相似的例子:

6.1 声明和定义
...
2 声明就是定义,除非
...
2.3 — 它在类定义中声明了一个非内联静态数据成员
...
示例:除以下一项外,所有都是定义:

诠释一个; // 定义一个
外部常量 int c = 1; // 定义 c
...

第二个示例与您的代码接近,但在使用extern 限定符方面存在显着差异。另外,请注意,上述声明(默认情况下)也是一个定义,除非列出的条件之一适用;我想说(尽管我不是语言律师)在您的情况下,这些条件都没有完全得到满足,所以您的声明也是一个定义。

注意:链接的文档只是一个草案标准;请务必阅读第一页底部的“免责声明”!

【讨论】:

  • 但是 /2.3 条件确实适用于此!
  • @DavisHerring 那是我的“灰色地带”。我认为它按字面意思应用,但术语“内联”可能有歧义:即 either 明确的inline 限定符“in body”(代码放置在类定义中)。不过,我可能只会增加混乱。
  • @AdrianMole,由于 2.3 中的 inline 不是代码格式的,我同意你的观点,non-inline 意味着 没有写在里面类定义,这意味着 2.3 不适用于我(好吧,Scott Meyers 的书)的案例。
  • @EnricoMariaDeAngelis 我同意,但正如我所说,我不是语言律师。这种对话很容易变成迭代的,如果不是递归的。此外,如果前两个例子是定义,那么我真的不明白为什么你的不是。
  • @EnricoMariaDeAngelis:该标准不使用“内联”来表示“在类中定义(或初始化)”。它也不使用代码字体来引用除关键字之外可以应用/暗示的 属性(如 inline 或 constexpr)。
【解决方案3】:

来自The Standard 章节“12.2.3.2 静态数据成员”:

如果该成员在程序中被 odr-used 并且命名空间范围定义不应包含初始化器,则该成员仍应在命名空间范围内定义。

通过使用它,它应该被定义。

【讨论】:

    猜你喜欢
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多