关于静态常量数据成员的声明和定义的混淆 [英] Confusion about declaration and definition of static const data memebers

查看:45
本文介绍了关于静态常量数据成员的声明和定义的混淆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<块引用>

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

那么示例代码是

class Widget {民众:静态常量 std::size_t MinVals = 28;//MinVals 的声明;...};...//没有定义.对于 MinValsstd::vector小部件数据;widgetData.reserve(Widget::MinVals);//使用 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};//从 C++11 开始const static int k;};const int X::k = 3;

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

以下 cppreference 示例也是如此:

<块引用>

struct X {静态常量 int n = 1;静态 constexpr int m = 4;};const int *p = &X::n, *q = &X::m;//X::n 和 X::m 是 odr 使用的const int X::n;//... 所以定义是必要的constexpr int X::m;//...(C++17 中的 X::m 除外)

我会说 static const int n = 1; 是一个定义,但它不是,基于倒数第二个评论.

解决方案

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

仅当该对象未使用 ODR(即未使用数据成员)时,仅声明就足够了在需要其地址存在的上下文中(例如绑定到引用或应用运算符 &).初始化器的存在等于定义.

在书中的例子中,很明显 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};//从 C++11 开始const static int k;};const int X::k = 3;

nm 是带有初始化器的声明.尝试获取 nm 的地址应该失败.

struct X {静态常量 int n = 1;静态 constexpr int m = 4;};const int *p = &X::n, *q = &X::m;const int X::n;constexpr int X::m;

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

Scott Meyers writes in Effective Modern C++, Item 30 page 210, that there's

no need to define integral static const data members in classes; declarations alone suffice,

then the sample code is

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

I was convinced that static const std::size_t MinVals = 28; is declaration and also a definition, as it is giving a value to MinVals, but the comment seems to claim that's only a declaration; the second comment actually claims there's no definition. The text after the code, indeed reads

MinVals lacks a definition.

Which confirms that static const std::size_t MinVals = 28; is not a definition, so I'm a bit confused.

cppreference doesn't help me much (my bold-italic):

If a static data member of integral or enumeration type is declared const (and not volatile), it can be initialized with an initializer in which every expression is a constant expression, right inside the class definition:

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

but first two lines in the class look definitions to me.

The same goes for a following example on 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)

where I'd have said static const int n = 1; is a definition, but it is not, based on the second to last comment.

解决方案

no need to define integral static const data members in classes; declarations alone suffice,

Declarations alone suffice only if that object is not ODR-used, that is, if a data member is not used in a context that would require its address to exist (like binding to a reference or applying operator &). The presence of an initializer does not equal a definition.

In the example from the book, it's clear that MinVals is not ODR-used, i.e., the compiler can use its value directly, without having to create an object in memory, and so the statement:

widgetData.reserve(Widget::MinVals);

becomes:

widgetData.reserve(28);

If, however, in any other place, MinVals were ODR-used, that would make the program ill-formed.

All other examples from cppreference clearly indicate when a value is ODR-used and a definition is required, and when not:

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

n and m are declarations with initializers. An attempt to obtain the address of either n or m should fail.

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;

Expressions &X::n and &X::m count as ODR-use of n and m, respectively (that is, an address is requested). For a constexpr static data members, a definition was required prior to C++17. From C++17, static constexpr data members are implicitly inline, which means, no out-of-class definition is needed, as they are definitions themselves.

这篇关于关于静态常量数据成员的声明和定义的混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆