开发者

Order of evaluation

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),
                   开发者_如何学Go currentPair_(nullptr),
                    lastPair_(nullptr)
    {/*e.b*/}


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 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.

Cheers & hth.,


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).

Note that this is only true for variables that are part of the same access specifier, so for instance variables found in a public: specifier may be initialized before or after those found in a private: specifier (the struct counts as a public: specifier, of course).

EDIT: the above paragraph was incorrect, I was thinking of allocation, not initialization:

9.2 §12: Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (class.access.spec).

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.


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.


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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜