评估顺序

发布于 2024-10-07 20:36:43 字数 489 浏览 0 评论 0原文

我想知道像这样的构造(初始化列表)是否具有明确定义的 EO(评估顺序):

struct MemoryManager
    {
        Pair* firstPair_;//<-beg
        Pair* currentPair_;
        Pair* lastPair_;//<-end

        MemoryManager():lastPair_(currentPair_ = firstPair_ = nullptr)
            {/*e.b.*/}
};

如果是,我个人更喜欢这种方式而不是更传统的方式:

    MemoryManager():firstPair_(nullptr),
                    currentPair_(nullptr),
                    lastPair_(nullptr)
    {/*e.b*/}

I wonder if construction like this (initialization list) has well defined EO (evaluation order):

struct MemoryManager
    {
        Pair* firstPair_;//<-beg
        Pair* currentPair_;
        Pair* lastPair_;//<-end

        MemoryManager():lastPair_(currentPair_ = firstPair_ = nullptr)
            {/*e.b.*/}
};

If yes I personally would prefer this way to the more conventional:

    MemoryManager():firstPair_(nullptr),
                    currentPair_(nullptr),
                    lastPair_(nullptr)
    {/*e.b*/}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

幸福不弃 2024-10-14 20:36:43

正如 John Dibling 所说,对于给定的具体示例,您的构造在技术上是正确的,但是它很脆弱,对于很多程序员来说很难理解。

脆弱性:

  • 如果更改声明顺序可能会失败。

  • 如果初始化列表发生更改,可能会失败。

要自行评估此类构造,请首先牢记以下想法:代码并不是要指示编译器执行您的命令;而是要让编译器执行您的命令。这是关于将您的意图传达给他人(也许还有您后来的自己)。

因此,尝试编写清晰代码。

As John Dibling has remarked, your construct is technically correct for the given concrete example, but it's brittle and it's hard to understand for many programmers.

Brittleness:

  • Can fail if the order of declaration is changed.

  • Can fail if the init list is changed.

To evaluate such constructs on your own, keep this idea foremost in your mind: code is not about instructing the compiler to do your bidding; it is about communicating your intent to others (and perhaps your later self).

Hence, try to write clear code.

仲春光 2024-10-14 20:36:43

。如代码所示,成员将按照它们在结构/类定义中声明的顺序进行初始化(构造函数定义中初始化程序的顺序无关紧要,最多您会收到警告告诉你它们的顺序不正确)。

12.6.2 §5:然后,非静态数据成员应按照它们在类定义中声明的顺序进行初始化(同样无论 mem 初始化程序的顺序如何)。

然而,更传统的方式有一个存在的理由,即如果变量声明的顺序发生变化(例如,由于重构),代码不会默默地中断。人们不会轻易认为声明的顺序是相关的,除非警告另有说明。

Yes. As shown in your code, the members will be initialized in the same order they are declared in the struct/class definition (the order of initializers in the constructor definition is irrelevant, at best you will get a warning telling you they are in an incorrect order).

12.6.2 §5: Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

However, the more conventional way has a reason to exist, namely that if the order of declaration of the variables changes (for instance, due to refactoring), the code will not break silently. People don't readily assume the order of declaration is relevant, unless a warning tells them otherwise.

秋风の叶未落 2024-10-14 20:36:43

如果你想这样做,那就按照每个人都能立即理解的方式去做,而不必浏览标准:

MemoryManager()
  // no initialization here
{
  lastPair_ = currentPair_ = firstPair_ = nullptr;
}

但是,我真的不明白这会给你带来什么

MemoryManager()
  : lastPair_(), currentPair_(), firstPair_()
{}

,因为它只用了大约六个字符就完成了完全相同的工作。

If you want to do this, then do it the way everybody understands immediately without having to browse the standard:

MemoryManager()
  // no initialization here
{
  lastPair_ = currentPair_ = firstPair_ = nullptr;
}

However, I don't really see what this buys you over

MemoryManager()
  : lastPair_(), currentPair_(), firstPair_()
{}

which does exactly the same in only about half a dozen more characters.

江湖正好 2024-10-14 20:36:43

对于这个特定示例,成员的初始化顺序无关紧要。以下构造函数将具有与问题中的构造函数相同的行为:

MemoryManager():firstPair_(lastPair_ = currentPair_ = nullptr)
{/*e.b.*/}

这是因为成员是 POD 等根本不由构造函数默认初始化 (12.6.2/4 C++ '03):

如果给定的非静态数据成员或基类不是由 mem-initializer-id 命名的(包括由于构造函数没有 ctor-initializer 而没有 mem-initializer-list 的情况),则

  • 如果实体是(可能是 cv 限定的)类类型(或其数组)或基类的非静态数据成员,并且实体类是非 POD 类,则实体默认初始化 (8.5) 。如果实体是 const 限定类型的非静态数据成员,则实体类应具有用户声明的默认构造函数。
  • 否则,实体不会被初始化。如果实体是 const 限定类型或引用类型,或者是包含(直接或间接)const 限定类型成员的(可能是 cv 限定的)POD 类类型(或其数组),则该程序是错误的 -成立。

对于上面的原始指针成员,“否则”项目符号适用。

现在,即使成员确实具有类类型,例如:

class MbrType {
public:
  MbrType();
  MbrType(int *);
  MbrType(MbrType const &);
  MbrType & operator=(MbrType const &);
};

然后,您编写的构造函数将导致成员具有您期望的值,但将允许非优化编译器将您的构造函数实现为

MemoryManager()
: firstPair_ ()  // implicit call to default constructor
, currentPair_ () // implicit call to default constructor
, lastPair_(currentPair_.operator=(firstPair_.operator=(MbrType (nullptr))))
{/*e.b.*/}

:调用 6 次而不是 3 次。

For this specific example, the initialization order for members is irrelevant. The following constructor would have the same behaviour as the one in the question:

MemoryManager():firstPair_(lastPair_ = currentPair_ = nullptr)
{/*e.b.*/}

This is because the members are POD and so are not default initialized by the constructor at all (12.6.2/4 C++ '03):

If a given nonstatic data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then

  • If the entity is a nonstatic data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non- static data member of a const-qualified type, the entity class shall have a user-declared default constructor.
  • Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.

For the raw pointer members above, the 'otherwise' bullet applies.

Now, even if the members did have class type, say:

class MbrType {
public:
  MbrType();
  MbrType(int *);
  MbrType(MbrType const &);
  MbrType & operator=(MbrType const &);
};

Then, the constructor as you've written it would result in the members having the values that you expect, but a non optimizing compiler would be allowed to implement your constructor as:

MemoryManager()
: firstPair_ ()  // implicit call to default constructor
, currentPair_ () // implicit call to default constructor
, lastPair_(currentPair_.operator=(firstPair_.operator=(MbrType (nullptr))))
{/*e.b.*/}

Resulting in 6 calls rather than 3.

清音悠歌 2024-10-14 20:36:43

,但这并不重要。您的代码不依赖于 currentPair_firstPair_ 归零的顺序。

No, but it doesn't matter. Your code does not depend on the order in which currentPair_ and firstPair_ are zeroed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文